友元

【1】友元常识点

(1)理论知识

<1> 友元分为友元类和友元函数,而友元函数又分为普通函数和成员函数。

<2> 友元具有三个性质:

[1] 不具有对称性:A是B的友元,不能说明B就是A的友元

[2] 不具有传递性:A是B的友元,B是C的友元,不可以认为A是C的友元

[3] 不具有继承性:继承关系中不存在友元的传递

(2)示例代码

<1> 友元类

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Base;
 5 
 6 class Test
 7 {
 8     friend class Base; // 友元类
 9 
10 public:
11     void display()
12     {
13         cout << "a = " << m_a << endl;
14         cout << "b = " << m_b << endl;
15     }
16 
17     void setBaseObj(Base* pBase)
18     {
19         m_pBaseObj = pBase;
20 //      m_pBaseObj->m_ca = 100;  // Error:使用未定义类型Base 即使m_ca是公有成员变量
21 //      auto tempObj = m_pBaseObj->m_ea;  //Error:使用未定义类型Base 成员Base::m_ea不可访问,因为m_ea是私有成员变量
22     }
23 
24 private:
25     Test(int x = 0, int y = 0, Base* p = nullptr) : m_a(x), m_b(y), m_pBaseObj(p)
26     {}
27 
28 private:
29     int m_a;
30     int m_b;
31 
32     Base* m_pBaseObj;
33 };
34 
35 class Base
36 {
37 public:
38     void display()
39     {
40         m_objt.display();
41         m_objt.m_a = 10;
42         m_objt.m_b = 20;
43         m_objt.display();
44     }
45 
46 public:
47     int m_ca;
48 
49 private:
50     Test m_objt;
51     int m_ea;
52 };
53 
54 int main()
55 {
56 //  Test objt; // Error:无法访问 private 成员(在“Test”类中声明) 构造函数私有访问限制
57     
58     Base b1;
59     b1.display();
60 
61     return 0;
62 }
63 
64 /*
65 a = 0
66 b = 0
67 a = 10
68 b = 20
69 */

 简单说明:

第8行:Base是Test的友元类,意味着Base类中可以访问Test类的私有成员,即41、42行的代码正常的理论基础。

友元不具有对称性,即Test类中无法访问Base类的私有成员。原因分析:

其实,根据实际逻辑理解,类的定义必定有先后之分。此示例中,Test类先于Base类进行定义。

尤其注意,即使Base类前置声明,但Test类中也仅可以声明Base类的指针、引用等,不能定义Base类型的对象。

可以想象一下,仅仅第4行执行过后,编译器只知道会有这样一个类型Base,但是这个类型到底内部长啥样子编译器还一概不知。

基于这种定义的先后顺序限制,必然导致Test类中无法通过Base类指针(m_pBaseObj)或引用访问其成员变量(第20、21行,注释Error的原因所在)。

<2> 普通函数作为友元函数

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Test
 5 {
 6 public:
 7     Test(int x = 0) : m_a(x)
 8     {}
 9 
10     void print();
11 
12     // 普通函数作为类Test的友元函数
13     friend void fun(Test & t);
14 
15 private:
16     int m_a;
17 };
18 
19 void Test::print()
20 {
21     cout << "this->m_a: " << m_a << endl;
22 }
23 
24 void fun(Test& t)
25 {
26     t.m_a = 100;    // 访问对象的私有成员变量m_a
27 }
28 
29 int main()
30 {
31     Test tt;
32     fun(tt);
33     tt.print();  // this->m_a: 100
34 
35     return 0;
36 }
37 
38 /*
39 this->m_a: 100
40 */

第13行:fun函数作为Test类的普通(与成员函数区分)函数,参数为Test类型对象的引用。

普通与成员函数从代码上如何区分?第24行,fun函数的定义时,没有加类的作用域(比如第19行的写法),视为普通函数。

第26行:fun函数定义时,如果内部想访问参数t的私有成员变量,理论上是禁止的。

解决方案:把fun函数定义为Test类的友元函数,即声明时加上friend关键字即可。

<3> 成员函数作为友元函数

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Test
 5 {
 6     int m_a;
 7 
 8 public:
 9     Test(int x = 0) : m_a(x)
10     {}
11 
12     void print();
13 
14     friend class Base; // 友元类
15 };
16 
17 void Test::print()
18 {
19     cout << "this->m_a: " << m_a << endl;
20 }
21 
22 class Base
23 {
24 public:
25     int m_b;
26     Base(int y = 0) : m_b(y)
27     {}
28 
29     void fun(Test& t);  // Base类的成员函数 作为 Test类的友元函数
30 };
31 
32 void Base::fun(Test& t)
33 {
34     t.m_a = 200;   // 访问对象的私有成员变量m_a
35 }
36 
37 int main()
38 {
39     Test t1;
40     Base b1;
41     b1.fun(t1);
42     t1.print();  // this->m_a: 200
43 
44     return 0;
45 }
46 
47 /*
48 this->m_a: 200
49 */

成员函数欲作为友元函数,必须先建立友元类关系基础。

因为成员函数作为友元函数,是基于友元类前提下的“肆无忌惮”。

如上示例:第14行,Base类是Test类的友元类,意味着Base类中可以随便访问Test类的所有成员变量。

因此,私认为Base类的fun成员函数是Test类的友元函数,尽管没有直接加friend关键字,但作用与友元函数相当。

第34行,可以随便访问私有成员变量m_a

[3] 理解友元

好基友,一辈子,关系好了要啥都随便哈。

 

Good Good Study, Day Day Up.

顺序 选择 循环 总结

posted @ 2013-01-04 14:58  kaizenly  阅读(610)  评论(0编辑  收藏  举报
打赏