目錄
友元
友元之普通函數形式
友元之成員函數形式
友元類
友元的特點
友元
-
什么叫友元?
一般來說,類的私有成員只能在類的內部訪問,類之外是不能訪問它們的。但如果將其他類/函數設置為類的友元,那么友元類/函數就可以在前一個類的類定義之外訪問其私有成員了。用friend關鍵字聲明友元。
將類比作一個家庭,類的private 成員相當于家庭的秘密,一般的外人當然不允許探聽這些秘密的,只有 friend 才有資格探聽這些秘密。
友元的三種形式:普通函數、成員函數、友元類
友元之普通函數形式
示例:程序中有Point類,需要求取兩個點的距離。按照設想,我們定義一個普通函數distance,接收兩個Point對象作為參數,通過公式計算這兩個點之間的距離。但Point的_ix和 _iy是私有成員,在類外不能通過對象訪問,那么可以將distance函數聲明為Point類的友元函數,之后就可以在distance函數中訪問Point的私有成員了。
class Point{
public:Point(int x, int y): _ix(x), _iy(y){}
?friendfloat distance(const Point & lhs, const Point & rhs);
private:int _ix;int _iy;
};
?
float distance(const Point & lhs, const Point & rhs){return sqrt((lhs._ix - rhs._ix)*(lhs._ix - rhs._ix) +(lhs._iy - rhs._iy)*(lhs._iy - rhs._iy));
}
友元之成員函數形式
假設類A有一個成員函數,該成員函數想去訪問另一個類B類中的私有成員變量。這時候則可以在第二個類B中,聲明第一個類A的那個成員函數為類B的友元函數,這樣第一個類A的某個成員函數就可以訪問第二個類B的私有成員變量了。
我們試驗一下,以另一種方式實現上面的需求,如果distance函數不再是一個普通函數,而是Line類的一個成員函數,也就是說需要在一個類(Line)的成員函數中訪問另一個類(Point)的私有成員,那么又該如何實現呢?
-
如果將Point類定義在Line類之前,Line類的成員函數要訪問Point類的私有成員,需要在Point類中將Line的這個成員函數設為友元函數——此時編譯器并不認識Line類;
-
如果將Line類定義在Point類之前,那么distance函數需要接受兩個const Point &作為參數——此時編譯器不認識Point類;
解決方法:
——在Line前面做一個Point類的前向聲明;
——但如果將distance的函數體寫在Line類中,編譯器雖然知道了有一個Point類,但并不知道Point類具體有什么成員,所以此時在函數體中訪問_ix、 _iy都會報錯,編譯器并不認識它們;
思考一下,有什么辦法可以解決這個問題呢?
class Point;//前行聲明
//只有前向聲明,只知道有piont這個類,不知道point是怎么實現的,但不能訪問point的內容,那么我們在line類中
//只做函數的聲明,不做定義
class Line
{//需要前向聲明,使編譯器知道有piont這個類public:float destance(const Point &lhs,const Point &rhs);
};
class Point
{public:Point(int x,int y):_ix(x),_iy(y){}//friend friend float Line::destance(const Point &lhs,const Point &rhs);
?private:int _ix;int _iy;
};
?
//point 有什么東西編譯器已經知道,現在對destance做定義就可以了,在外面是普通函數,就上作用域
float Line::destance(const Point &lhs,const Point &rhs)
{return sqrt(pow(lhs._ix - rhs._ix ,2)+pow(lhs._iy - rhs._iy,2));
}
補充:
前向聲明的用處:進行了前向聲明的類,可以以引用或指針的形式作為函數的參數,只要不涉及到對該類對象具體成員的訪問,編譯器可以通過。
(讓編譯器認識這個類,但是注意如果只進行前向聲明,這個類的具體實現沒有的話,無法使用這個類的對象,無法創建)
注意:友元的聲明要注意和函數的形式完全對應上。
友元類
如上的例子,假設類 Line 中不止有一個 distance 成員函數,還有其他成員函數,它們都需要訪問Point 的私有成員,如果還像上面的方式一個一個設置友元,就比較繁瑣了,可以直接將 Line 類設置為 Point 的友元類,在工作中這也是更常見的方法。
class Point {//...friend class Line;//...
};
在Point類中聲明Line類是本類的友元類,那么Line類中的所有成員函數中都可以訪問Point類的私有成員。一次聲明,全部解決。
不可否認,友元將類的私有成員暴露出來,在一定程度上破壞了信息隱藏機制,似乎是種“副作用很大的藥”,但俗話說“良藥苦口”。好工具總是要付出點代價的,拿把鋒利的刀砍瓜切菜,總是要注意不要割到手指的。
友元的存在,使得類的接口擴展更為靈活,使用友元進行運算符重載從概念上也更容易理解一些,而且, C++ 規則已經極力地將友元的使用限制在了一定范圍內。
友元的特點
-
友元不受類中訪問權限的限制——可訪問私有成員
-
友元破壞了類的封裝性
-
不能濫用友元 ,友元的使用受到限制
-
友元是單向的——A類是B類的友元類,則A類成員函數中可以訪問B類私有成員;但并不代表B類是A類的友元類,如果A類中沒有聲明B類為友元類,此時B類的成員函數中并不能訪問A類私有成員
-
友元不具備傳遞性——A是B的友元類,B是C的友元類,無法推斷出A是C的友元類
-
友元不能被繼承——因為友元破壞了類的封裝性,為了降低影響,設計層面上友元不能被繼承