友元的位置关系

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

 

 

原文地址:https://www.cnblogs.com/benxintuzi/p/4543693.html