有些情況下,允許特定的非成員函數訪問一個類的私有成員,同時仍阻止一般的訪問,這是很方便做到的。例如被重載的操作符,如輸入或輸出操作符,經常需要訪問類的私有數據成員。
友元(frend)機制允許一個類將對其非公有成員的訪問權授予指定的函數或者類,友元的聲明以friend開始,它只能出現在類定義的內部,友元聲明可以出現在類中的任何地方:友元不是授予友元關系的那個類的成員,所以它們不受其聲明出現部分的訪問控制影響。通常,將友元聲明成組地放在類定義的開始或結尾是個好主意。
友元函數是指某些雖然不是類成員函數卻能夠訪問類的所有成員的函數。類授予它的友元特別的訪問權,這樣該友元函數就能訪問到類中的所有成員。
#includeusing namespace std; class A { public: friend void set_show(int x, A &a); //該函數是友元函數的聲明 private: int data; }; void set_show(int x, A &a) //友元函數定義,為了訪問類A中的成員 { a.data = x; cout << a.data << endl; } int main(void) { class A a; set_show(1, a); return 0; }
友元類的所有成員函數都是另一個類的友元函數,都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員)。當希望一個類可以存取另一個類的私有成員時,可以將該類聲明為另一類的友元類。
關於友元類的注意事項:
(1) 友元關系不能被繼承。
(2) 友元關系是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的聲明。
(3) 友元關系不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明。
#includeusing namespace std; class A { public: friend class C; //這是友元類的聲明 private: int data; }; class C //友元類定義,為了訪問類A中的成員 { public: void set_show(int x, A &a) { a.data = x; cout< 3、友元成員函數
使類B中的成員函數成為類A的友元函數,這樣類B的該成員函數就可以訪問類A的所有成員了。
當用到友元成員函數時,需注意友元聲明和友元定義之間的相互依賴,在該例子中,類B必須先定義,否則類A就不能將一個B的函數指定為友元。然而,只有在定義了類A之後,才能定義類B的該成員函數。更一般的講,必須先定義包含成員函數的類,才能將成員函數設為友元。另一方面,不必預先聲明類和非成員函數來將它們設為友元。
#includeusing namespace std; class A; //當用到友元成員函數時,需注意友元聲明與友元定義之間的互相依賴。這是類A的聲明 class B { public: void set_show(int x, A &a); //該函數是類A的友元函數 }; class A { public: friend void B::set_show(int x, A &a); //該函數是友元成員函數的聲明 private: int data; void show() { cout << data << endl; } }; void B::set_show(int x, A &a) //只有在定義類A後才能定義該函數,畢竟,它被設為友元是為了訪問類A的成員 { a.data = x; cout << a.data << endl; } int main(void) { class A a; class B b; b.set_show(1, a); return 0; }
友元小結:
在需要允許某些特定的非成員函數訪問一個類的私有成員(及受保護成員),而同時仍阻止一般的訪問的情況下,友元是可用的。
優點:
可以靈活地實現需要訪問若干類的私有或受保護的成員才能完成的任務;
便於與其他不支持類概念的語言(如C語言、匯編等)進行混合編程;
通過使用友元函數重載可以更自然地使用C++語言的IO流庫。
缺點:
一個類將對其非公有成員的訪問權限授予其他函數或者類,會破壞該類的封裝性,降低該類的可靠性和可維護性。
參考資料:
1、《C++ Prime》第4版 第12章類的友元小節