C++友元函式和友元類(C++ friend)詳解
私有成員只能在類的成員函式內部訪問,如果想在別處訪問物件的私有成員,只能通過類提供的介面(成員函式)間接地進行。這固然能夠帶來資料隱藏的好處,利於將來程式的擴充,但也會增加程式書寫的麻煩。
C++ 是從結構化的C語言發展而來的,需要照顧結構化設計程式設計師的習慣,所以在對私有成員可訪問範圍的問題上不可限制太死。
C++ 設計者認為, 如果有的程式設計師真的非常怕麻煩,就是想在類的成員函式外部直接訪問物件的私有成員,那還是做一點妥協以滿足他們的願望為好,這也算是眼前利益和長遠利益的折中。因此,C++ 就有了友元(friend)的概念。打個比方,這相當於是說:朋友是值得信任的,所以可以對他們公開一些自己的隱私。
友元分為兩種:友元函式和友元類。
友元函式
在定義一個類的時候,可以把一些函式(包括全域性函式和其他類的成員函式)宣告為“友元”,這樣那些函式就成為該類的友元函式,在友元函式內部就可以訪問該類物件的私有成員了。
將全域性函式宣告為友元的寫法如下:
friend 返回值型別 函式名(引數表);
將其他類的成員函式宣告為友元的寫法如下:
friend 返回值型別 其他類的類名::成員函式名(引數表);
但是,不能把其他類的私有成員函式宣告為友元。
關於友元,看下面的程式示例。
- #include<iostream>
- using namespace std;
- class CCar; //提前宣告CCar類,以便後面的CDriver類使用
- class CDriver
- {
- public:
- void ModifyCar(CCar* pCar); //改裝汽車
- };
- class CCar
- {
- private:
- int price;
- friend int MostExpensiveCar(CCar cars[], int total); //宣告友元
- friend void CDriver::ModifyCar(CCar* pCar); //宣告友元
- };
- void CDriver::ModifyCar(CCar* pCar)
- {
- pCar->price += 1000; //汽車改裝後價值增加
- }
- int MostExpensiveCar(CCar cars[], int total) //求最貴氣車的價格
- {
- int tmpMax = -1;
- for (int i = 0; i<total; ++i)
- if (cars[i].price > tmpMax)
- tmpMax = cars[i].price;
- return tmpMax;
- }
- int main()
- {
- return 0;
- }
這個程式只是為了展示友元的用法,所以 main 函式什麼也不做。
第 3 行聲明瞭 CCar 類,CCar 類的定義在後面。之所以要提前宣告,是因為 CDriver 類的定義中用到了 CCar 型別(第7行),而此時 CCar 類還沒有定義,編譯會報錯。
不要第 3 行,而把 CCar 類的定義寫在 CDriver 類的前面,是解決不了這個問題的,因為 CCar 類中也用到了 CDriver 型別(第14行),把 CCar 類的定義寫在前面會導致第 14 行的 CDriver 因沒有定義而報錯。C++ 為此提供的解決辦法是:可以簡單地將一個類的名字提前宣告,寫法如下:
class 類名;
儘管可以提前宣告,但是在一個類的定義出現之前,仍然不能有任何會導致該類物件被生成的語句。但使用該類的指標或引用是沒有問題的。
第 13 行將全域性函式 MostExpensiveCar 宣告為 CCar 類的友元,因此在第 24 行可以訪問 cars[i] 的私有成員 price。同理,第 14 行將 CDriver 類的 ModifyCar 成員函式宣告為友元,因此在第 18 行可以訪問 pCar 指標所指向的物件的私有成員變數 price。
友元類
一個類 A 可以將另一個類 B 宣告為自己的友元,類 B 的所有成員函式就都可以訪問類 A 物件的私有成員。在類定義中宣告友元類的寫法如下:
friend class 類名;
來看如下例程:
- class CCar
- {
- private:
- int price;
- friend class CDriver; //宣告 CDriver 為友元類
- };
- class CDriver
- {
- public:
- CCar myCar;
- void ModifyCar() //改裝汽車
- {
- myCar.price += 1000; //因CDriver是CCar的友元類,故此處可以訪問其私有成員
- }
- };
- int main()
- {
- return 0;
- }
第 5 行將 CDriver 宣告為 CCar 的友元類。這條語句本來就是在宣告 CDriver 是一個類,所以 CCar 類定義前面就不用宣告 CDriver 類了。第 5 行使得 CDriver 類的所有成員函式都能訪問 CCar 物件的私有成員。如果沒有第 5 行,第 13 行對 myCar 私有成員 price 的訪問就會導致編譯錯誤。
一般來說,類 A 將類 B 宣告為友元類,則類 B 最好從邏輯上和類 A 有比較接近的關係。例如上面的例子,CDriver 代表司機,CCar 代表車,司機擁有車,所以 CDriver 類和 CCar 類從邏輯上來講關係比較密切,把 CDriver 類宣告為 CCar 類的友元比較合理。
友元關係在類之間不能傳遞,即類 A 是類 B 的友元,類 B 是類 C 的友元,並不能匯出類 A 是類 C 的友元。“咱倆是朋友,所以你的朋友就是我的朋友”這句話在 C++ 的友元