友元
【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.
顺序 选择 循环 总结