类并非只能拥有友元函数,也可以将类作为友元。这样友元类的所有方法都可以访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。
友元声明可以位于公有、私有或保护部分,其所在的位置无关紧要。
下面给一个友元类的例子:
class Tv
{
int state;
int volume;
int maxchannel;
int channel;
int mode;
int input;
public:
friend class Remote;
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(s), mode(Cable), input(TV) {}
~Tv() {};
void onoff() {state = (state == On) ? Off : On;}
bool ison() const {return state == On;}
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 setting() const;
};
class Remote
{
int mode;
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 set_chan(Tv & t, int n) {t.channel = n;}
void chanup(Tv & t) {t.chanup();}
void chandown(Tv & t) {t.chandown();}
void set_mode(Tv & t) {t.set_mode();}
void set_input(Tv & t) {t.set_input();}
};
从例子中的代码可知,大多数的Remote方法都是用Tv类的公有接口实现的。这意味着这些方法不是真正需要作为友元。唯一需要作为友元的方法是直接访问Tv类成员的函数set_chan(),如果只让该方法作为类的友元,必须小心排列类中各种声明和定义的顺序。
void set_chan(Tv & t, int n)
类Tv是set_chan的参数,所以Tv声明应在Remote之前,但是Tv中又提到了Remote,这意味着Remote应在Tv之前。这就有些矛盾了
避开这种循环依赖的方法是:使用向前声明
class Tv;
class Remote {......};
class Tv {.....};
不能像下面这样排列
class Remote;
class Tv {.....};
class Remote {......};
编译器在Tv类的声明中看到Remote的一个方法被声明为Tv类的友元之前,应该先看到Remote类的声明和set_chan()方法的声明。
还有一个问题,Remote声明包含了内联代码,例如:
void chanup(Tv & t) {t.chanup();}
这将调用一个Tv的方法,所以编译器这时必须已经看到了Tv类的声明。结果是Tv声明位于Remote声明的后面,解决这个问题的办法是,使Remote声明中只包含方法的声明,并将实际的定义放在Tv类之后。
class Tv;
class Remote {......};
class Tv {.....};
inline void Remote::chanup(Tv & t) {t.chanup();}//依然是内联的
共同的友元
需要使用友元的另一种情况是,函数需要访问两个类的私有数据。这时可以将同一个函数声明为两个类共同的友元函数。