參考《c++ Primer Plus》 第6版15.1ios
C++控制對類對象私有部分的訪問,在外部沒法直接訪問類的私有或保護成員。一般,公有類方法提供惟一的訪問途徑。有時這種限制太嚴格,不適合特定的編程問題。因此C++提供了友元這種形式,經過讓函數或類成爲類的友元,能夠賦予該函數或類與類的成員函數具備相同的訪問權限。c++
友元的做用在於提升程序的運行效率(即減小了類型檢查和安全性檢查等都須要的時間開銷),可是,它破壞了類的封裝性和隱藏性,使得非成員函數能夠訪問類的私有成員。編程
(1)友元函數安全
(2)友元類函數
(3)友元成員函數this
在類定義體中由關鍵字friend加以修飾說明的非成員函數spa
#include <iostream> using namespace std; class Window { public: Window(int x, int y, int h, int w) { X=x, Y=y, H=h, W=w; } friend long Area(Window & WinObj); //在類中聲明出友員函數的原型 int getH() { return H; } int getW() { return W; } private: int X,Y,H,W; }; long Area(Window & WinObj) //在類外定義出友員函數的函數體 { return (long)WinObj.H*WinObj.W; } //實現經過對象訪問類中的private成員,由於對窗口的面積的計算最簡單的方式應該是H*W。 int main() { Window winA(10,10,300,300); cout <<"Window Area is "<< endl; cout <<"Window Area square: "<< Area(winA)<< endl; return 0; }
運行結果:設計
Window Area is Window Area square: 90000
友員函數與成員函數在編程方面的不一樣點:
① 友員函數在類體內應該採用friend加以限定,但成員函數不須要。
② 友員函數體在類體外不須要採用"類名::"加以限定。
③ 調用它時不須要採用"對象名.成員名"方式,而是直接調用它。
④ 因爲友員函數不是成員函數,於是無this指針,在函數體中訪問對象中的成員時必須經過對象名(可將形參定義爲引用),而在成員函數體內能夠直接訪問成員數據。指針
註釋:code
類的友元函數是非成員函數,其訪問權限與成員函數相同。
能夠將類做爲友元,友元類的全部方法均可以訪問原始類的私有成員和保護成員。
以下 friend class Remote; 聲明Remote爲Tv類的友元類:
//tv.h -- Tv and Remote classes #ifndef TV_H_ #define TV_H_ class Tv { public: friend class Remote; //Remote can access Tv private parts enum State{Off,On}; enum {MinVal,MaxVal=20}; enum {Antenna,Cable}; enum {TV,DVD}; Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc),channel(12),mode(Cable),input(TV) {} void onoff() {state=(state==On)?Off:On;} bool ison() const {return state==On;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode() {mode=(mode==Antenna)?Cable:Antenna;} void set_input() {input=(input==TV)?DVD:TV;} void settings() const; //display all settings private: int state; int volume; int maxchannel; int channel; int mode; int input; }; class Remote { private: int mode; public: Remote(int m = Tv::TV): mode(m){}; bool volup(Tv &t){return t.volup();} bool voldown(Tv & t){return t.voldown();} void onoff(Tv & t){t.onoff();} void chanup(Tv & t){t.chanup();} void chandown(Tv & t){t.chandown();} void set_chan(Tv & t,int c){t.channel=c;} void set_mode(Tv & t){t.set_mode();} void set_input(Tv & t){t.set_input();} };
在Remote類中,只有set_channel(Tv & tv, int ch)方法直接訪問Tv類的私有成員,因此能夠選擇只讓這個方法成爲類的友元,而沒必要讓Remote整個類成爲友元。不過這麼作必須當心排列各類聲明和定義的順序。
讓Remote::set_channel()成爲Tv類的友元方法是,在Tv類中將其聲明爲友元:
//tvfm.h #ifndef TVFM_H_ #define TVFM_H_ class Tv; class Remote { public: enum State{Off,on}; enum {MinVal,MaxVal=20}; enum {Antenna,Cable}; enum {TV,DVD}; private: int mode; public: Remote(int m=TV):mode(m) {} bool volup(Tv &t); bool voldown(Tv & t); void onoff(Tv & t); void chanup(Tv & t); void chandown(Tv & t); void set_mode(Tv & t); void set_input(Tv & t); void set_chan(Tv & t,int c); }; class Tv { public: friend void Remote::set_chan(Tv & t,int c); enum State{Off,On}; enum {MinVal,MaxVal=20}; enum {Antenna,Cable}; enum {TV,DVD}; Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc),channel(12),mode(Cable),input(TV) {} void onoff() {state=(state==On)?Off:On;} bool ison() const {return state==On;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode() {mode=(mode==Antenna)?Cable:Antenna;} void set_input() {input=(input==TV)?DVD:TV;} void settings() const; //display all settings private: int state; int volume; int maxchannel; int channel; int mode; int input; }; //Remote methods as inline functions inline bool Remote::volup(Tv & t){return t.volup();} inline bool Remote::voldown(Tv & t){return t.voldown();} inline void Remote::onoff(Tv & t){t.onoff();} inline void Remote::chanup(Tv & t){t.chanup();} inline void Remote::chandown(Tv & t){t.chandown();} inline void Remote::set_mode(Tv & t){t.set_mode();} inline void Remote::set_input(Tv & t){t.set_input();} inline void Remote::set_chan(Tv & t,int c) {t.channel=c;} #endif
① 在封裝和快速性兩方面合理選擇----OOP中類的主要優勢是能夠實現數據隱藏與保護,即不容許非成員函數對它訪問,這樣能夠提升數據使用的安全性。但在訪問數據的效率方面則降低(由於正常時應該經過public型的方法來訪問)。但在某些應用場合下,非成員函數體中須要經過對象名直接訪問類中的private成員,以達到高速高效率地訪問數據,這能夠經過友員函數來實現。
② 有些函數須要放在類的外面或者類設計完之後再加以補充的,此時可能不能設計爲類中的成員函數,可是又須要訪問類中的私有成員。