[关闭]
@cxm-2016 2016-09-29T11:01:04.000000Z 字数 2911 阅读 1699

C++:友元

c++

版本:1
作者:陈小默
说明:修改了部分错别字和标点

类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所有方法都可以访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只有特定成员函数可以是另一个类的友元。[1]

友元类


这里我们以遥控器与电视机的关系为例,电视机与遥控器有关系,但是其关系不是继承的is-a关系,同样也不可能是包含has-a关系。所以这里提出了一种新的联系方式:友元类。

现在我们设计一款电视,这个电视不保留除开机键外的一切按键,所有一切都由遥控器控制,也就是要求遥控器能够访问电视的私有成员,遥控器又作为电视机的用户接口。

  1. #ifndef primer_tv_h
  2. #define primer_tv_h
  3. #include<iostream>
  4. class Tv{
  5. private:
  6. enum {LESS=0,LARGE=100,CHANNEL=64};//音量范围0-100,频道数量255
  7. bool _power;//电源接通状态
  8. bool _switch;//待机切换状态
  9. int _voice;//声音
  10. int _channel;//频道
  11. void switch_(){
  12. if(_power){//只有电源接通状况下才可以切换待机状态
  13. _switch = !_switch;
  14. if(_switch) show();
  15. }
  16. }
  17. void voiceUp(){
  18. if(_switch){//调大音量
  19. if(_voice < LARGE) _voice++;
  20. show();
  21. }
  22. }
  23. void voiceDown(){
  24. if(_switch){//调小音量
  25. if(_voice > LESS) _voice--;
  26. show();
  27. }
  28. }
  29. void channelPiror(){
  30. if(_switch){//向前换台
  31. _channel = (_channel-1)%LARGE;
  32. show();
  33. }
  34. }
  35. void channelNext(){
  36. if(_switch){//向后换台
  37. _channel = (_channel+1)%LARGE;
  38. show();
  39. }
  40. }
  41. void channel(int c){//切换到某一频道
  42. if(c<0)
  43. _channel = 0;
  44. else if(c>CHANNEL)
  45. _channel = CHANNEL;
  46. else
  47. _channel = c;
  48. show();
  49. }
  50. void show(){//显示节目状态
  51. std::cout<<"~~~当前电视频道:"<<_channel<<" ,电视音量:"<<_voice<<"~~~"<<endl;
  52. }
  53. public:
  54. friend class Remote;//声明友元遥控器
  55. Tv(){
  56. _power = false;
  57. _switch = false;
  58. _voice = 20;
  59. _channel = 0;
  60. }
  61. ~Tv(){}
  62. void powerOn(){
  63. _power = true;
  64. }
  65. void powerOff(){
  66. _switch = false;
  67. _power = false;
  68. }
  69. };
  70. class Remote{
  71. private:
  72. Tv *_tv;
  73. public:
  74. Remote(Tv *tv):_tv(tv){};
  75. ~Remote(){}
  76. void switch_(){_tv->switch_();}
  77. void voiceUp(){_tv->voiceUp();}
  78. void voiceDown(){_tv->voiceDown();}
  79. void channelPiror(){_tv->channelPiror();}
  80. void channelNext(){_tv->channelNext();}
  81. void channel(int c){_tv->channel(c);}
  82. };
  83. #endif

现在我们来测试一下

  1. #include"stdafx.h"
  2. #include "tv.h"
  3. int main(int argc, const char * argv[]) {
  4. cout<<"买了一个小米的新电视"<<endl;
  5. Tv *mi = new Tv();
  6. cout<<"打开箱子一看发现了一台遥控器"<<endl;
  7. Remote mi_r(mi);
  8. cout<<"我想看电视于是使用遥控器打开开关"<<endl;
  9. mi_r.switch_();
  10. cout<<"发现电视并没有什么反应,仔细检查后发现一激动忘记接电源了,"<<"现在接通电源"<<endl;
  11. mi->powerOn();
  12. cout<<"再用遥控器打开"<<endl;
  13. mi_r.switch_();
  14. cout<<"果然看到节目了,累了一天打算看新闻联播放松放松,由于知道13频道是新闻频道,所以直接在遥控器上输入13"<<endl;
  15. mi_r.channel(133);
  16. cout<<"为什么是64频道,仔细一看是自己多按了一个3,于是自己小心的又按了一遍13"<<endl;
  17. mi_r.channel(13);
  18. cout<<"啊,看到新闻联播后突然很困,就想把声音关小一点"<<endl
  19. <<"然后我就一直一直按减小音量键"<<endl;
  20. for(int i=0;i<30;i++){
  21. mi_r.voiceDown();
  22. }
  23. cout<<"看来我确实是太困了,都按了这么多下按键,算了待机睡觉吧"<<endl;
  24. mi_r.switch_();
  25. cout<<"我看看待机后按键有没有用"<<endl;
  26. mi_r.channelPiror();
  27. mi_r.channelNext();
  28. mi_r.voiceDown();
  29. cout<<"按了三个按键都没反应,看来真待机了,为了省电我要关闭电源键"<<endl;
  30. mi->powerOff();
  31. cout<<"可以安心睡觉了"<<endl;
  32. return 0;
  33. }

从这个例子中我们可以看出,友元声明可以位于公有、私有或者保护部分,其所在的位置无关紧要。由于我们在Remote类中使用Tv类,所以必须在Remote类前声明class Tv

友元成员函数


假如我们现在有一台老式的电视,这台电视提供了许多公有的按键。于是我们的遥控器访问这些方法的时候并不需要真正的友元,唯一需要成为友元方法的是channel方法,因为电视机上不可能会给你提供数字按键

  1. class Tv{
  2. private:
  3. ...
  4. void channel(int c);//切换到某一频道
  5. void show();//显示节目状态
  6. public:
  7. ...
  8. void voiceUp();
  9. void voiceDown();
  10. void channelPiror();
  11. void channelNext();
  12. };

那么我们仅仅需要让Remote.channel(int c)的方法成为Tv的友元:

  1. class Tv{
  2. ...
  3. friend void Remote::channel(int c);
  4. };

然而,要使编译器能够处理这条指令,它必须知道Remote的定义:

  1. class Remote;
  2. class Tv{
  3. ...
  4. friend void Remote::channel(int c);
  5. };

共同的友元


最简单的例子,我们的遥控器不仅仅只能控制一台电视机的。所以我们可以给两台Tv声明同一个遥控器作为友元类。


[1] Stephen Prata.C++ Primer Plus 6th.人民邮电出版社.2015.3 602-611
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注