이번 포스팅에서는 friend class가 필요한 상황과 사용 방법을 간단하게 정리하려고 한다.
목차
- 프렌드 클래스(friend class)란?
- 프렌드 클래스(friend class)사용 방법
- 전체 코드
프렌드 클래스 (friend class)란?
프렌드 클래스(friend class)란, class에 대한 friend 선언은 original class의 private과 protected의 접근 권한을 또 다른 class에 넘겨주는 것을 뜻한다. 흔히 C++에서 friend하면 friend function만 연상하기 쉽지만, class 또한 friend로 나타낼 수 있다. class에 friend를 사용하는 상황은 리모콘과 TV의 관계를 떠올리면 쉽다. TV에 대한 리모콘은 is-a 관계도 아니며 has-a 관계도 아니다. TV는 리모콘이 아니며 TV는 리모콘을 소유하고 있지도 않다. 즉 둘은 완전히 다른 개별적인 개체들이지만 리모콘은 TV의 기능을 조작하며 다룰 수 있다. 이럴 경우에 class를 friend로 선언해서 사용하게 된다.
프렌드 클래스 (friend class) 사용 방법
friend class는 original class의 private member와 protected member에 접근할 수 있다. 그리고 더욱 제한을 가해서 특정 class의 member function만 다른 class의 friend가 되도록 할 수도 있다. 다음 코드는 TV와 remote의 관계를 설명하는 friend class의 예제이다.
tv.h
// tv.h -- 푸바오 Tv class, Remote class
#ifndef __TV_H__
#define __TV_H__
/// this is Tv class
class Tv
{
public:
friend class Remote; // Remote class에게 Tv 내부 접근 권한을 허용한다.
enum {Off, On};
enum {MinVal, MaxVal = 20};
enum {Antenna, Cable};
enum {TV, DVD};
Tv(int s = Off, int mc = 125) : state(s), volume(5),
maxchannel(mc), channel(2), mode(Cable), input(TV) { }
void onoff() { state = (state == On) ? Off : On; }
bool ison() const { return state == 0; }
bool volup();
bool voldown();
void chanup();
void chandown();
void set_mode() { mode = (mode = Antenna) ? Cable : Antenna; }
void set_input() { input = (input == TV) ? DVD : TV; }
void settings() const; // 모든 설정값들의 상태를 출력한다.
private:
int state; // On , Off
int volume; // 디지털 볼륨이라고 가정
int maxchannel; // 최대 채널 수
int channel; // 현재 설정된 채널
int mode; // 지상파 방송 or 케이블 방송
int input; // TV Input or DVD Input
};
// Remote class friend to Tv class
class Remote
{
private:
int mode; // TV 조종모드 or DVD 조종모드
public:
Remote(int m = Tv::TV) : mode(m) { }
bool volup(Tv& t) { return t.volup(); }
bool voldown(Tv& t) { return t.voldown(); }
void onoff(Tv& t) { t.onoff(); }
void chanup(Tv& t) { t.chanup(); }
void chandown(Tv& t) { t.chandown(); }
void set_chan(Tv& t, int c) { t.channel = c; }
void set_mode(Tv& t) { t.set_mode(); }
void set_input(Tv& t) { t.set_input(); }
};
#endif
- friend class Remote;
Tv class 내부에 있는 private와 protected의 대한 접근 권한을 remote에게 부여한다. (friend 선언)
위치에 따른 차이는 없으니 어디에 선언해도 상관은 없다. - void onoff() { state = (state == On) ? Off : On; }
state의 상태에 따라 조건 연산자를 통해 상태를 변환시킨다.
특정 function들만 다른 class들에 대해 friend로 만들기
이전에 사용된 코드에서 remote는 실제적으로 Tv class의 private에 접근하는 함수는 remote class의 set_chan() 뿐이다. 그래서 불필요하게 class 자체를 friend로 선언하는거보다 remote가 필요로하는 tv의 함수만 friend로 선언해줄 수 있다.
class Tv
{
friend void Remote::set_chan(Tv & t, int c);
...
}
이렇게하면 기존에 선언해주었던 friend class Remote; 를 생략하고 remote가 필요로 하는 tv 기능만 사용 권한을 넘겨줄 수 있다. 하지만 tv내의 friend void remote:: 부분은 Remote class보다 상단에 선언되어 있어 remote::set_chan 함수를 컴파일러가 인식하지 못한다. 그러므로 사전 선언(forward declaration)을 통해 Tv보다 먼저 Remote를 선언해서 컴파일러에게 알려주어야 한다.
Remote의 사전 선언을 통해 위에 문제를 해결한 모습
class Remote;
class Tv
{
friend void Remote::set_chan(Tv & t, int c);
...
}
class Remote
{
void set_chan(Tv & t, int c);
...
}
전체 코드
아래는 Tv와 Remote 의 관계를 friend class 활용해 구현한 모든 코드이다.
Tv.h
// tv.h -- 푸바오 Tv class, Remote class
#ifndef __TV_H__
#define __TV_H__
/// this is Tv class
class Tv
{
public:
friend class Remote; // Remote class에게 Tv 내부 접근 권한을 허용한다.
enum {Off, On};
enum {MinVal, MaxVal = 20};
enum {Antenna, Cable};
enum {TV, DVD};
Tv(int s = Off, int mc = 125) : state(s), volume(5),
maxchannel(mc), channel(2), mode(Cable), input(TV) { }
void onoff() { state = (state == On) ? Off : On; }
bool ison() const { return state == 0; }
bool volup();
bool voldown();
void chanup();
void chandown();
void set_mode() { mode = (mode = Antenna) ? Cable : Antenna; }
void set_input() { input = (input == TV) ? DVD : TV; }
void settings() const; // 모든 설정값들의 상태를 출력한다.
private:
int state; // On , Off
int volume; // 디지털 볼륨이라고 가정
int maxchannel; // 최대 채널 수
int channel; // 현재 설정된 채널
int mode; // 지상파 방송 or 케이블 방송
int input; // TV Input or DVD Input
};
// Remote class friend to Tv class
class Remote
{
private:
int mode; // TV 조종모드 or DVD 조종모드
public:
Remote(int m = Tv::TV) : mode(m) { }
bool volup(Tv& t) { return t.volup(); }
bool voldown(Tv& t) { return t.voldown(); }
void onoff(Tv& t) { t.onoff(); }
void chanup(Tv& t) { t.chanup(); }
void chandown(Tv& t) { t.chandown(); }
void set_chan(Tv& t, int c) { t.channel = c; }
void set_mode(Tv& t) { t.set_mode(); }
void set_input(Tv& t) { t.set_input(); }
};
#endif
Tv.cpp
// tv.h -> Tv class 용
#include <iostream>
#include "tv.h"
bool Tv::volup()
{
if (volume < MaxVal)
{
volume++;
return true;
}
else
return false;
}
bool Tv::voldown()
{
if (volume > MinVal)
{
volume--;
return true;
}
else
return false;
}
void Tv::chanup()
{
if (channel < maxchannel)
channel++;
else
channel = 1;
}
void Tv::chandown()
{
if (channel > 1)
channel--;
else
channel = maxchannel;
}
void Tv::settings() const
{
using std::cout;
using std::endl;
cout << "TV = " << (state == Off ? "Off" : "ON") << endl; // 조건 연산자 사용
if (state == On)
{
cout << "볼륨 = " << volume << endl;
cout << "채널 = " << channel << endl;
cout << "모드 = "
<< (mode == Antenna ? "지상파 방송" : "케이블 방송") << endl;
cout << "입력 = " << (input == TV ? "TV" : "DVD") << endl;
}
}
use_tv.cpp
// use_Tv.cpp -- Tv와 Remote 클래스를 사용한다
#include <iostream>
#include "tv.h"
int main(void)
{
using std::cout;
Tv s42;
cout << "s42 Tv초기 설정값: \n";
s42.settings();
s42.onoff();
s42.chanup();
cout << "s42 Tv의 변경된 설정값:\n";
s42.settings();
Remote grey;
grey.set_chan(s42, 10);
grey.volup(s42);
grey.volup(s42);
cout << "리모콘 사용 후 s42 Tv의 설정값:\n";
s42.settings();
Tv s58(Tv::On);
s58.set_mode();
grey.set_chan(s58, 28);
cout << "s58 Tv의 설정값:\n";
s58.settings();
return 0;
}
'과거 자료' 카테고리의 다른 글
[C++ Basic] Smart Pointer Template Class (0) | 2022.07.10 |
---|---|
[C++ Basic] 예외(exception) 메커니즘 (0) | 2022.07.07 |
[#2] Stack (0) | 2022.07.04 |
[#1] Singly Linked List (0) | 2022.06.25 |
[C++ Basic] 클래스 템플릿 (Class Template) (0) | 2022.06.21 |