• 友元的位置关系


    考虑如下一种场景:有一个电视机类Tv和一个遥控器类Remote,如何定义二者的关系呢?首先遥控器不能继承电视机,因为不是is-a关系;其次,遥控器也并非电视机的一部分,因此包含关系has-a也不适合。这时候将Remote类作为Tv类的友元类比较合适,使其能够使用Tv类的任何数据来控制电视机。但是如何定义两个类的位置关系呢?

     

    首先是类的定义,稍后做出解释:

    bt_友元类的位置关系.h

     1 #ifndef TV_ROMOTE_H
     2 #define TV_REMOTE_H
     3 #include <iostream>
     4 
     5 class Tv
     6 {
     7 public:
     8     friend class Remote;                      // 遥控器作为电视机的一个友元类
     9     enum{OFF, ON};                            // 电视机的开关状态
    10     enum{MINVAL = 0, MAXVAL = 50};            // 电视机的音量范围
    11     enum{MINCHANNEL = 1, MAXCHANNEL = 100};   // 电视机的频道范围
    12 
    13     Tv(int s = OFF, int v = 15) : state(s), volume(v), channel(1){ }
    14     void onOff(){ state = (state == ON) ? OFF : ON; }   // 开关机
    15     bool volumeUp();                 // 音量控制
    16     bool volumeDown();
    17     void channelUp();                // 频道控制
    18     void channelDown();
    19     void settings() const;           // 显示所有当前设置
    20 
    21 private:
    22     int state;
    23     int volume;
    24     int channel;
    25 };
    26 bool Tv::volumeUp()
    27 {
    28     if(volume < MAXVAL)
    29     {
    30         volume++;
    31         return true;
    32     }
    33     else
    34         return false;
    35 }
    36 bool Tv::volumeDown()
    37 {
    38     if(volume > MINVAL)
    39     {
    40         volume--;
    41         return true;
    42     }
    43     else
    44         return false;
    45 }
    46 
    47 void Tv::channelUp()
    48 {
    49     if(channel < MAXCHANNEL)
    50         channel++;
    51     else
    52         channel = MINCHANNEL;    // 在最大频道时执行Up操作立即返回MINCHANNEL
    53 }
    54 void Tv::channelDown()
    55 {
    56     if(channel > MINCHANNEL)
    57         channel--;
    58     else
    59         channel = MAXCHANNEL;   // 在最小频道时执行Down操作立即跳到MAXCHANNEL
    60 }
    61 
    62 void Tv::settings() const
    63 {
    64     using std::cout;
    65     using std::endl;
    66     cout << "电视机当前状态为:" << (state == ON ? "开启" : "关闭") << endl;
    67     cout << "当前音量设置为:" << volume << endl;
    68     cout << "当前频道设置为:" << channel << endl;
    69 }
    70 
    71 class Remote
    72 {
    73 public:
    74     bool volumeUp(Tv& tv){ return tv.volumeUp(); }
    75     bool volumeDown(Tv& tv){ return tv.volumeDown(); }
    76     void onOff(Tv& tv){ tv.onOff(); }
    77     void channelUp(Tv& tv){ tv.channelUp(); }
    78     void channelDown(Tv& tv){ tv.channelDown(); }
    79     void setChannel(Tv& tv, int chan);
    80 };
    81 void Remote::setChannel(Tv& tv, int chan)
    82 {
    83     tv.channel = chan;
    84 }
    85 
    86 #endif // TV_ROMOTE_H

    测试用例:

     1 #include "bt_友元类的位置关系.h"
     2 #include <iostream>
     3 
     4 int main()
     5 {
     6     using std::cout;
     7     using std::endl;
     8     Tv tv;                  // 实例化一台电视机
     9     tv.settings();
    10 
    11     cout << endl;
    12     tv.onOff();             // 打开电视机
    13     tv.settings();
    14 
    15     cout << endl;
    16     Remote remote;          // 实例化一个遥控器
    17     remote.volumeDown(tv);  // 用遥控器降低音量
    18     remote.channelUp(tv);   // 用遥控器增加频道
    19     remote.setChannel(tv, 10);  // 用遥控器切换到10频道
    20     tv.settings();
    21 
    22     cout << endl;
    23     remote.onOff(tv);       // 用遥控器关闭电视机
    24     tv.settings();
    25 
    26     return 0;
    27 }
    
    

    如上所示,Tv的友元类必须定义在Tv类的后边,如果将Remote类的定义放在Tv类之前,那么由于友元类要使用Tv类的引用变量,那么此时编译器还没有看见过Tv类,造成编译失败。

     

    仔细分析上边Remote的函数,大多数都是通过Tv类的公共接口实现的,就是说根本不需要友元类,一个普通类也可以实现,只有setChannel()函数是直接访问Tv类的私有成员的。因此,其实让Remote::setChannel()函数成为Tv类的友元函数即可,让其他函数保持普通函数的身份,那么此时又如何安排Tv类和Remote类的相对位置关系呢?

    若如之前那样安排,就得如下表示:

    class Tv

    {

        friend void Remote::setChannel(Tv& tv, int chan);

    }

    class Remote{ ... }

    当编译器编译Tv类时,它无法明确得知Remote是一个类,因此出现“’Remote’ has not been declared”。而若此时将Remote类定义移到Tv类之前,又会出现Remote中引用的Tv未定义,此时为了避免这种循环依赖关系,可以使用前向声明(forward declaration),如:

     1 class Tv;
     2 class Remote
     3 {
     4 public:
     5 bool volumeUp(Tv& tv);
     6 bool volumeDown(Tv& tv);
     7     void onOff(Tv& tv);
     8     void channelUp(Tv& tv);
     9     void channelDown(Tv& tv);
    10     void setChannel(Tv& tv, int chan);
    11 };
    12 class Tv{ ... }
    13 
    14 void Remote::setChannel(Tv& tv, int chan)
    15 {
    16     tv.channel = chan;
    17 }

    注意:使用前向声明Tv时,Remote只能使用Tv来声明函数,而不能在定义中具体使用Tv的功能,因为在编译器看到完整的Tv类之前,是无法确定Tv中具体有什么的,因此,必须将Remote中引用Tv类的函数实现放到Tv类的定义后进行。

     

    友元类和友元函数的示意图如下:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    说明:使用友元类时不需要前向声明,因为友元语句(friend class Remote;)本身告诉编译器Remote是一个类,而使用友元函数时就需要把这一点补上。

     

     

  • 相关阅读:
    使用mysqltools配置读写分离环境
    mysql group replication 主节点宕机恢复
    django ---- models继承
    django -- 对模式进行调式(pay with the api)
    django -- 多对多关系的实现
    django -- verbose_name的对数据库层面的影响
    django -- model中只有Field类型的数据才能成为数据库中的列
    django -- 为model 指定数据库名
    django -- 联合索引
    flask基础之jijia2模板使用基础(二)
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4543693.html
Copyright © 2020-2023  润新知