类并非只能拥有友元函数还可以将类作为友元,友元被赋予从类外访问类的私有部分的权限,在这种情况下,友元类的所有方法都可以访问原始类的公有/私有/保护成员,也能指定特定的成员函数为一个类的友元.
什么时候希望一个类成为另一个类的友元? 书上举得一个栗子:
完整代码
假设编写一个模拟遥控器和电视机的程序,决定有TV和Remote类表示电视机和遥控器,显然这两个类存在着某种关系(净说废话).但电视机不是遥控器,反之亦然,所以正常的公有继承is-a关系并不适用.遥控器并非电视机的一部分.所以,包含私有和保护集成的has-a关系也不适用.但众所周知遥控器可以改变电视机的状态.这说明遥控器应该是电视机类的一个友元.
友元的声明:
友元声明可以位于公有,私有或者保护部分,所在其位置无关紧要.但由于Remote类提到了TV类所以必须先定义Tv类,或者使用前向声明.这样来声明一个友元类:
1 friend class Remote;
|
|
从上面的从书中摘抄的毫无诚意的代码中可以看出,所有的Remote方法都是Tv类的友元,似乎Remote类除了构造函数都使用了Tv类的公有接口,但是事上唯一直接访问了Tv类成员的Remote类方法是Remote::set_chan()
,那么就可以选择仅特定的类成员成为另一个类的友元.但这样就要小心的排列声明和各种定义;
让Remote::set_chan()
成为Tv类的友元的方法是,在Tv类声明中将其定义为友元:
|
|
就酱,但是吧……编译器要处理这句话首先得知道Remote的定义,不然编译器不知道Remote是个类,所以这就要把Remote类的声明挪到Tv类声明前面,但Remote类用了Tv对象..这就又得把Tv类定义挪到Remote类定义前面去.咦等等.那Remote咋办他需要在Tv类的前面啊.这..这就很尴尬了,呐,避免这种死循环的方法是使用 前向声明(forward declaration):
那能不能这样?:
这是不行的,在编译器在Tv类中的声明中看到Remote类的一个方法称为Tv类的友元之前,该先让编译器看到Remote类的声明和Remote::set_chan()
函数的声明.
好了,但在Remote类中可以看到,有些方法包含了内联代码,例如: void onoff(Tv & t){t.onoff();}
由于它使用了一个Tv的方法,所以在此之前编译器必须看到Tv类声明,但是Tv类在Remote类后面声明..解决方法就是把函数定义放在Tv类之后就成..
吼,现在只有一个Remote方法是Tv类的友元了; 编译器一开始通过前向类型得知了Tv是个类,在读取声明并编译了这些方法之后,使用lnline
关键字仍然可以使Remote类未定义的函数称为内联方法.完整代码
其他友元关系
遥控器能影响电视.现在你想通过电视对遥控器产生某种影响,这可以让类彼此成为对方的友元来实现; 需要记住的是对于使用Remote类对象的Tv方法,其 函数原型 可以在Remote类声明之前声明,但必须在Remote类之后定义,这样编译器才有足够的信息来编译该方法.
由于Remote声明在Tv后面,所以可以在类声明中定义volup();buzz()可以再Tv中声明,但必须在Remote后面定义;
共同友元
函数需要访问两个类的私有数据,函数可以是一个类的成员,另一个类的友元; 也可以是两个类的友元: