zoukankan      html  css  js  c++  java
  • 友元的位置关系

    考虑如下一种场景:有一个电视机类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是一个类,而使用友元函数时就需要把这一点补上。

     

     

  • 相关阅读:
    UVALive 6909 Kevin's Problem 数学排列组合
    UVALive 6908 Electric Bike dp
    UVALive 6907 Body Building tarjan
    UVALive 6906 Cluster Analysis 并查集
    八月微博
    hdu 5784 How Many Triangles 计算几何,平面有多少个锐角三角形
    hdu 5792 World is Exploding 树状数组
    hdu 5791 Two dp
    hdu 5787 K-wolf Number 数位dp
    hdu 5783 Divide the Sequence 贪心
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4543693.html
Copyright © 2011-2022 走看看