@cxm-2016
2016-09-29T11:01:04.000000Z
字数 2911
阅读 1720
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,频道数量255
bool _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声明同一个遥控器作为友元类。