티스토리 뷰
주의 사항!
- 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
- 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
- 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.
클래스에 대한 friend 선언
클래스를 선언할 때 보통 멤버 변수들을 private으로 선언하고, 멤버 함수들을 public으로 선언해 왔습니다. private으로 선언한 멤버 변수는 클래스 외 다른 곳에서는 절대 접근할 수 없었습니다. 그런데 friend 선언을 통해 일부 다른 클래스에서 해당 클래스의 private 변수에 접근이 가능하도록 할 수 있습니다.
예를 들어보겠습니다. A클래스와 B클래스, 두 클래스가 선언되었습니다. 그리고 A클래스 안에서 B클래스를 friend 선언을 했습니다. 그렇게 되면 A클래스는 B클래스를 자신의 친구로 여기기 때문에 B클래스가 자신의 private 변수들에 접근하는 것을 허용합니다. 그렇다고 A클래스 또한 B클래스의 private 변수에 접근할 수 있게 되는 것은 아닙니다. B클래스는 A클래스를 '친구'로 생각하지 않기 때문입니다. A클래스 또한 B클래스의 private 변수에 접근할 수 있도록 하려면 B클래스 안에서 A클래스를 friend 선언해야 합니다.
다음 예제를 통해 friend 선언의 사용법을 알아보겠습니다.
#include <iostream>
#include <cstring>
using namespace std;
class Girl; //Girl 이라는 이름의 클래스가 있음을 알림
class Boy
{
private:
int height;
friend class Girl;
public:
Boy(int len) : height(len) {}
void ShowYourFriendInfo(Girl& frn);
};
class Girl
{
private:
char phNum[20];
public:
Girl(const char* num)
{
strcpy(phNum, num);
}
void ShowYourFriendInfo(Boy& frn);
friend class Boy;
};
void Boy::ShowYourFriendInfo(Girl& frn)
{
cout << "Her phone number : " << frn.phNum << endl;
}
void Girl::ShowYourFriendInfo(Boy& frn)
{
cout << "His height : " << frn.height << endl;
}
int main(void)
{
Boy boy(170);
Girl girl("010-1234-5678");
boy.ShowYourFriendInfo(girl);
girl.ShowYourFriendInfo(boy);
return 0;
}
/*
실행결과
Her phone number : 010-1234-5678
His height : 170
*/
Boy 클래스에서는 Girl 클래스를 friend 선언하고, Girl 클래스에서는 Boy 클래스를 friend 선언합니다. 그런데 Boy클래스가 Girl 클래스를 friend 선언하려면 그전에 먼저 Girl 클래스가 선언되어 있어야 할 것 같고, 그렇다고 Girl 클래스를 먼저 선언하려니 이 역시 Boy 클래스를 먼저 선언해야 이를 friend 선언할 수 있을 것 같습니다. 그래서 예제에서와 같이 Girl이라는 이름의 클래스가 있다는 것을 알려주는 코드를 넣었습니다.
그런데 해당 코드 줄이 없어도 정상적으로 컴파일이 가능합니다. 왜냐하면 friend 선언할 때 해당 클래스가 있다는 것을 알리는 역할도 같이 하기 때문입니다.
그리고 예제를 보면 Boy 클래스의 멤버 함수 ShowYourFriendInfo를 Girl 클래스의 선언까지 마친 후 클래스 밖에서 정의했습니다. 이유는 다음과 같습니다. Boy 클래스의 해당 함수는 Girl 클래스의 멤버 변수인 phNum에 접근합니다. 그런데 Girl 클래스가 정의되어 있지 않으니 해당 클래스가 있는 줄은 알아도 클래스 안에 phNum이 뭐하는 녀석인지는 알 수가 없습니다. 그렇게 되면 컴파일 오류가 발생하기 때문에 Girl 클래스의 정의까지 마친 후 해당 함수를 정의했습니다.
그리고 Boy 클래스에서는 private으로 Girl 클래스를 friend 선언하였고 Girl 클래스에서는 public으로 Boy 클래스를 friend 선언했음을 알 수 있습니다. 이렇듯 friend 선언은 어디에도 선언이 가능합니다.
정보은닉을 해치는 friend
사실 C++에서 friend 선언은 많은 논란이 되었습니다. 좋은 클래스의 조건 중 하나로 '정보은닉'에 대해 배웠습니다. friend 선언은 이 정보은닉을 무너뜨리는 문법입니다. 따라서 friend 선언을 무분별하게 사용하게 될 경우 아주 위험할 수 있습니다. 따라서 friend 선언은 되도록 사용하지 않는 연습을 하라고 이 책의 저자는 말하고 있습니다.
함수에 대한 friend 선언
전역 함수를 대상으로도, 클래스의 멤버 함수를 대상으로도 friend 선언이 가능합니다. 이에 관한 예제를 살펴보겠습니다.
#include <iostream>
using namespace std;
class Point;
class PointOP
{
private:
int opcnt;
public:
PointOP() : opcnt(0) {}
Point PointAdd(const Point&, const Point&);
Point PointSub(const Point&, const Point&);
~PointOP()
{
cout << "Operation times : " << opcnt << endl;
}
};
class Point
{
private:
int x;
int y;
public:
Point(const int& xpos, const int& ypos) : x(xpos), y(ypos) {}
friend Point PointOP::PointAdd(const Point&, const Point&);
friend Point PointOP::PointSub(const Point&, const Point&);
friend void ShowPointPos(const Point&);
};
Point PointOP::PointAdd(const Point& pnt1, const Point& pnt2)
{
opcnt++;
return Point(pnt1.x + pnt2.x, pnt1.y + pnt2.y);
}
Point PointOP::PointSub(const Point& pnt1, const Point& pnt2)
{
opcnt++;
return Point(pnt1.x - pnt2.x, pnt1.y - pnt2.y);
}
int main(void)
{
Point pos1(1, 2);
Point pos2(2, 4);
PointOP op;
ShowPointPos(op.PointAdd(pos1, pos2));
ShowPointPos(op.PointSub(pos1, pos2));
return 0;
}
void ShowPointPos(const Point& pos)
{
cout << "x : " << pos.x << ", y : " << pos.y << endl;
}
/*
실행결과
x : 3, y : 6
x : -1, y : -2
Operation times : 2
*/
예제의 Point 클래스에 friend 선언된 함수들을 보겠습니다.
friend Point PointOP::PointAdd(const Point&, const Point&);
friend Point PointOP::PointSub(const Point&, const Point&);
friend void ShowPointPos(const Point&);
PointAdd와 PointSub 함수는 PointOP 클래스의 함수입니다. 그리고 ShowPointPos 함수는 전역 함수입니다. friend 선언한 함수에서 Point 클래스의 멤버 변수에 접근할 수 있음을 확인할 수 있습니다.
'공부 일지 > CPP 공부 일지' 카테고리의 다른 글
C++ | 상속 (0) | 2021.08.02 |
---|---|
C++ | static과 클래스 (0) | 2021.08.02 |
C++ | const와 객체 (0) | 2021.08.02 |
C++ | 복사 생성자와 explicit 키워드 (0) | 2021.08.02 |
C++ | 클래스와 배열 그리고 this 포인터 (0) | 2021.08.02 |