
클래스의 friend 선언
이렇듯 friend 선언은 private 멤버의 접근을 허용하는 선언이다.
그러나 friend 선언은 정보은닉에 반하는 선언이기 때문에 매우 제한적으로 선언되어야 한다!
예시를 살펴보자.

위 클래스의 ShowYourFriendInfo 함수 내에서 Boy 클래스의 private 멤버인 height에 직접 접근을 하고 있다. Girl 클래스는 Boy 클래스의 friend이므로, Girl 클래스 내에 선언된 모든 멤버함수는 이렇듯 Boy 클래스의 private 멤버에 접근이 가능하다. 그럼 이번에는 위의 두 클래스를 기반으로 예제를 하나 작성해 살펴보자. Boy 클래스와 Girl 클래스가 서로를 friend로 선언하도록 하겠다.
MyFriendClass.cpp
#include <iostream>
#include <cstring>
using namespace std;
class Girl; //Girl이라는 이름이 클래스의 이름임을 알림
class Boy
{
private:
int height;
friend class Girl; //Girl 클래스에 대한 friend 선언
public:
Boy(int len) : height(len)
{ }
void ShowYourFriendInfo(Girl &frn);
};
class Girl
{
private:
char phNum[20];
public:
Girl(char *num)
{
strcpy(phNum, num);
}
void ShowYourFriendInfo(Boy &frn);
friend class Boy; //Boy 클래스에 대한 friend 선언
};
void Boy::ShowYourFriendInfo(Girl &frn)
{
cout<<"His 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;
}
His phone number: 010-1234-5678
His height: 170
friend 선언은 언제?
앞서 말했듯이, friend 선언은 객체지향의 대명사 중 하나인 '정보은닉'을 무너뜨린다.
friend 선언은 지나치면 아주 위험할 수 있다. friend 선언은 필요한 상황에서 극히 소극적으로 사용해야 한다.
friend 선언이 좋은 약으로 사용되는 상황은, 이후에 연산자 오버로딩을 공부하면서 보게 될 것이다.
함수의 friend 선언
전역함수를 대상으로도, 클래스의 멤버함수를 대상으로도 friend 선언이 가능하다. 예제를 통해 알아보자.
MyFriendFunction.cpp
#include <iostream>
using namespace std;
class Point; //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<<", ";
cout<<"y: "<<pos.y<<endl;
}
x: 3, y: 6
x: -1, y: -2
Operation times: 2
위의 예제를 살펴보면,
class Point; //Point가 클래스의 이름임을 선언
14, 15행에 있는,
Point PointAdd(const Point&, const Point&);
Point PointSub(const Point&, const Point&);
이를 컴파일 하기 위해서는 Point가 클래스의 이름임을 컴파일러에게 알려줘야 한다. 그런데 Point 클래스는 뒤에서 등장하기 때문에 이렇게 별도로 Point가 클래스의 이름임을 선언해야 한다.
friend Point PointOP::PointAdd
(const Point&, const Point&);
friend Point PointOP::PointSub
(const Point&, const Point&);
이렇듯 클래스의 특정 멤버함수를 대상으로도 friend 선언이 가능하다.
friend void ShowPointPos(const Point&);
};
이는 전역함수 ShowPointPos에 대해 friend 선언을 하고 있다.
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);
}
PointAdd와 PointSub 함수는 Point 클래스의 friend로 선언되었기 때문에 private 멤버에 접근이 가능하다.
void ShowPointPos(const Point& pos)
{
cout<<"x: "<<pos.x<<", ";
cout<<"y: "<<pos.y<<endl;
}
ShowPointPos 함수도 Point 클래스의 friend로 선언되었기 때문에 private 멤버에 접근이 가능하다.