@cxm-2016
2016-09-29T03:01:04.000000Z
字数 2911
阅读 2084
c++
版本:1
作者:陈小默
说明:修改了部分错别字和标点
类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所有方法都可以访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只有特定成员函数可以是另一个类的友元。[1]
这里我们以遥控器与电视机的关系为例,电视机与遥控器有关系,但是其关系不是继承的is-a关系,同样也不可能是包含has-a关系。所以这里提出了一种新的联系方式:友元类。
现在我们设计一款电视,这个电视不保留除开机键外的一切按键,所有一切都由遥控器控制,也就是要求遥控器能够访问电视的私有成员,遥控器又作为电视机的用户接口。
#ifndef primer_tv_h#define primer_tv_h#include<iostream>class Tv{private:enum {LESS=0,LARGE=100,CHANNEL=64};//音量范围0-100,频道数量255bool _power;//电源接通状态bool _switch;//待机切换状态int _voice;//声音int _channel;//频道void switch_(){if(_power){//只有电源接通状况下才可以切换待机状态_switch = !_switch;if(_switch) show();}}void voiceUp(){if(_switch){//调大音量if(_voice < LARGE) _voice++;show();}}void voiceDown(){if(_switch){//调小音量if(_voice > LESS) _voice--;show();}}void channelPiror(){if(_switch){//向前换台_channel = (_channel-1)%LARGE;show();}}void channelNext(){if(_switch){//向后换台_channel = (_channel+1)%LARGE;show();}}void channel(int c){//切换到某一频道if(c<0)_channel = 0;else if(c>CHANNEL)_channel = CHANNEL;else_channel = c;show();}void show(){//显示节目状态std::cout<<"~~~当前电视频道:"<<_channel<<" ,电视音量:"<<_voice<<"~~~"<<endl;}public:friend class Remote;//声明友元遥控器Tv(){_power = false;_switch = false;_voice = 20;_channel = 0;}~Tv(){}void powerOn(){_power = true;}void powerOff(){_switch = false;_power = false;}};class Remote{private:Tv *_tv;public:Remote(Tv *tv):_tv(tv){};~Remote(){}void switch_(){_tv->switch_();}void voiceUp(){_tv->voiceUp();}void voiceDown(){_tv->voiceDown();}void channelPiror(){_tv->channelPiror();}void channelNext(){_tv->channelNext();}void channel(int c){_tv->channel(c);}};#endif
现在我们来测试一下
#include"stdafx.h"#include "tv.h"int main(int argc, const char * argv[]) {cout<<"买了一个小米的新电视"<<endl;Tv *mi = new Tv();cout<<"打开箱子一看发现了一台遥控器"<<endl;Remote mi_r(mi);cout<<"我想看电视于是使用遥控器打开开关"<<endl;mi_r.switch_();cout<<"发现电视并没有什么反应,仔细检查后发现一激动忘记接电源了,"<<"现在接通电源"<<endl;mi->powerOn();cout<<"再用遥控器打开"<<endl;mi_r.switch_();cout<<"果然看到节目了,累了一天打算看新闻联播放松放松,由于知道13频道是新闻频道,所以直接在遥控器上输入13"<<endl;mi_r.channel(133);cout<<"为什么是64频道,仔细一看是自己多按了一个3,于是自己小心的又按了一遍13"<<endl;mi_r.channel(13);cout<<"啊,看到新闻联播后突然很困,就想把声音关小一点"<<endl<<"然后我就一直一直按减小音量键"<<endl;for(int i=0;i<30;i++){mi_r.voiceDown();}cout<<"看来我确实是太困了,都按了这么多下按键,算了待机睡觉吧"<<endl;mi_r.switch_();cout<<"我看看待机后按键有没有用"<<endl;mi_r.channelPiror();mi_r.channelNext();mi_r.voiceDown();cout<<"按了三个按键都没反应,看来真待机了,为了省电我要关闭电源键"<<endl;mi->powerOff();cout<<"可以安心睡觉了"<<endl;return 0;}
从这个例子中我们可以看出,友元声明可以位于公有、私有或者保护部分,其所在的位置无关紧要。由于我们在Remote类中使用Tv类,所以必须在Remote类前声明class Tv。
假如我们现在有一台老式的电视,这台电视提供了许多公有的按键。于是我们的遥控器访问这些方法的时候并不需要真正的友元,唯一需要成为友元方法的是channel方法,因为电视机上不可能会给你提供数字按键
class Tv{private:...void channel(int c);//切换到某一频道void show();//显示节目状态public:...void voiceUp();void voiceDown();void channelPiror();void channelNext();};
那么我们仅仅需要让Remote.channel(int c)的方法成为Tv的友元:
class Tv{...friend void Remote::channel(int c);};
然而,要使编译器能够处理这条指令,它必须知道Remote的定义:
class Remote;class Tv{...friend void Remote::channel(int c);};
最简单的例子,我们的遥控器不仅仅只能控制一台电视机的。所以我们可以给两台Tv声明同一个遥控器作为友元类。
