为什么会有友元类?
与友元函数存在的功能类似 ,当两个或多个类之间不存在派生或者继承关系的时候,但又想在某一个类中调用另外一个类的方法,此时可以用 友元类。友元类的所有方法都可以访问原始类的私有成员和保护成员。
循环依赖:
这里创建两个类,一个是电视类、另外一个是遥控器类。并将遥控器类作为电视类的友元类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #ifndef __TV_H__ #define __TV_H__
#include <iostream>
using namespace std;
class Tv { private: enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD};
int state; int volume; int channel; int input;
public: Tv(int s = off) : state(s), volume(5), channel(2), input(TV){} void onoff(){state = (state == on) ? off : on;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_input(){input = (input == TV) ? DVD : TV;} void show_settings() const;
friend class Remote; };
class Remote { private: int mode; public: Remote(int m = Tv::TV) : mode(m){} void onoff(Tv &t){t.onoff();} bool volup(Tv &t){return t.volup();} bool voldown(Tv &t){return t.voldown();} void chanup(Tv &t){t.chanup();} void chandown(Tv &t){t.chandown();} void set_channel(Tv &t, int c){t.channel = c;} void set_input(Tv &t){t.set_input();} };
#endif
|
上面这个类书写存在的一些问题:
只在Remote的void set_channel(Tv &t, int c){t.channel = c;}中访问到了Tv类的私有成员channel ,其他的都是在调用Tv类的公有接口,那这样的话将Remote定义为友元类就大可不必,可用只将Remote中的set_channel设置为友元就可用,做法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #ifndef __TV_H__ #define __TV_H__
#include <iostream>
using namespace std;
class Tv { private: enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD};
int state; int volume; int channel; int input;
public: Tv(int s = off) : state(s), volume(5), channel(2), input(TV){} void onoff(){state = (state == on) ? off : on;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_input(){input = (input == TV) ? DVD : TV;} void show_settings() const;
friend void Remote::set_channel(Tv &t, int c); };
class Remote { private: int mode; public: Remote(int m = Tv::TV) : mode(m){} void onoff(Tv &t){t.onoff();} bool volup(Tv &t){return t.volup();} bool voldown(Tv &t){return t.voldown();} void chanup(Tv &t){t.chanup();} void chandown(Tv &t){t.chandown();} void set_channel(Tv &t, int c){t.channel = c;} void set_input(Tv &t){t.set_input();} };
#endif
|
上面程序存在的问题?
存在的问题是循环依赖,解决方法是前向声明。
循环依赖解释?
类Tv在编译的时候会调用friend void Remote::set_channel(Tv &t, int c);而此时不知道,Remote是什么也不知道其中的set_channel方法是什么。
倘若将两个类交换顺序能否解决问题?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #ifndef __TV_H__ #define __TV_H__
#include <iostream>
using namespace std;
class Tv;
class Remote { private: int mode; public: Remote(int m = Tv::TV) : mode(m){} void onoff(Tv &t){t.onoff();} bool volup(Tv &t){return t.volup();} bool voldown(Tv &t){return t.voldown();} void chanup(Tv &t){t.chanup();} void chandown(Tv &t){t.chandown();} void set_channel(Tv &t, int c){t.channel = c;} void set_input(Tv &t){t.set_input();} };
class Tv { private: enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD};
int state; int volume; int channel; int input;
public: Tv(int s = off) : state(s), volume(5), channel(2), input(TV){} void onoff(){state = (state == on) ? off : on;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_input(){input = (input == TV) ? DVD : TV;} void show_settings() const;
friend void Remote::set_channel(Tv &t, int c); };
#endif
|
即使交换顺序并且加上class Tv声明能解决问题吗?
答案是否定的因为Remote类中的构造函数 用到了 Tv::TV 即便前面声明了Tv是类,但是此时也无法知道Tv中又什么成员变量(即无法知道TV是什么)
解决办法 将Tv类中的枚举类型在 Remote类中也来一份,并且使用Remote自己的枚举成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #ifndef __TV_H__ #define __TV_H__
#include <iostream>
using namespace std; class Tv;
class Remote { private: int mode;
enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD}; public: Remote(int m = TV) : mode(m){}
void onoff(Tv &t){t.onoff();} bool volup(Tv &t){return t.volup();} bool voldown(Tv &t){return t.voldown();} void chanup(Tv &t){t.chanup();} void chandown(Tv &t){t.chandown();} void set_channel(Tv &t, int c){t.channel = c;} void set_input(Tv &t){t.set_input();} };
class Tv { private: enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD};
int state; int volume; int channel; int input;
public: Tv(int s = off) : state(s), volume(5), channel(2), input(TV){} void onoff(){state = (state == on) ? off : on;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_input(){input = (input == TV) ? DVD : TV;} void show_settings() const;
friend void Remote::set_channel(Tv &t, int c); };
#endif
|
上面这样写就没事了吗?
答案是否定的,存在问题,类Remote中成员函数实现中,调用了Tv类的方法,而此时Tv类没有编译,所以会报错。正确做法是,将Remote中接口的实现定义在类外面也就是 类Tv的后面。如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #ifndef __TV_H__ #define __TV_H__
#include <iostream>
using namespace std;
class Tv;
class Remote { private: int mode;
enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD}; public: Remote(int m = TV) : mode(m){} void onoff(Tv &t); bool volup(Tv &t); bool voldown(Tv &t); void chanup(Tv &t); void chandown(Tv &t); void set_channel(Tv &t, int c); void set_input(Tv &t); };
class Tv { private: enum{off, on}; enum{MinVal, MaxVal = 20}; enum{MinChan = 1, MaxChan = 100}; enum{TV, DVD};
int state; int volume; int channel; int input;
public: Tv(int s = off) : state(s), volume(5), channel(2), input(TV){} void onoff(){state = (state == on) ? off : on;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_input(){input = (input == TV) ? DVD : TV;} void show_settings() const;
friend void Remote::set_channel(Tv &t, int c); };
inline void Remote::onoff(Tv &t){t.onoff();} inline bool Remote::volup(Tv &t){return t.volup();} inline bool Remote::voldown(Tv &t){return t.voldown();} inline void Remote::chanup(Tv &t){t.chanup();} inline void Remote::chandown(Tv &t){t.chandown();} inline void Remote::set_channel(Tv &t, int c){t.channel = c;} inline void Remote::set_input(Tv &t){t.set_input();}
#endif
|
总结:
以上就是友元类,产生循环依赖的解决办法。思想来源c++primer plus 15章
以上是Remote 对Tv 做出一些控制 ,用的方法是将Remote声明为Tv的友元,且由于只有其中一个方法调用了Tv的私有成员,其余都是调用公有接口,那为了封装的安全性,只将Remote中调用Tv类中私有成员的方法声明为友元即可。而实际有可能存在互为友元的情况,即遥控器对电视做出控制,电视并给一个反馈到遥控器,这样就要求两个类互相为友元。
补充:
共同友元 ! 要使用友元的另一种情况是,函数需要访问两个类的私有数据。从逻辑上说,这样的函数是两个类的成员函数,但这是不可能的。做法就是共同友元。
应用场景:
有关类A是某可用编程的测量设备 ,B是某分析仪器,要求两者有共同的时钟,这样就可用声明两个友元函数,类A中将B 的成员函数声明为友元,类B中将A 的成员函数声明为友元,