天天看點

對c++友元函數和友元類的了解

1,友元函數的定義和作用

我們已知道類具有封裝和資訊隐藏的特性。隻有類的成員函數才能通路類的私有成員,程式中的其他函數是無法通路私有成員的。非成員函數可以通路類中的公有成員,但是如果将資料成員都定義為公有的,這又破壞了隐藏的特性。另外,應該看到在某些情況下,特别是在對某些成員函數多次調用時,由于參數傳遞,類型檢查和安全性檢查等都需要時間開銷,而影響程式的運作效率。

為了解決上述問題,提出一種使用友元的方案。友元是一種定義在類外部的普通函數,但它需要在類體内進行說明,為了與該類的成員函數加以差別,在說明時前面加以關鍵字friend。友元不是成員函數,但是它可以通路類中的私有成員。友元的作用在于提高程式的運作效率(即減少了類型檢查和安全性檢查等都需要的時間開銷),但是,它破壞了類的封裝性和隐藏性,使得非成員函數可以通路類的私有成員。

2,上述的通路不是直接通路,對于普通私有成員變量是通過對象通路,對于私有靜态變量是通過類通路。

為什麼不能直接通路呢?

先了解一下為什麼成員函數可以直接通路成員變量?

成員函數能夠通路類的成員變量是因為傳遞了指向目前對象的this指針,它如果通路資料成員對其操作是this指向的對象的資料成員,是有實際意義的 。

友元函數不是成員函數,沒有傳遞隐藏的this指針,隻能間接通路。

這點其實和靜态成員函數一樣,靜态成員函數也是沒有this指針的,是以它隻能通路靜态成員變量或者通過對象通路非靜态成員變量。

例子:

class Rect  
{  
public:  
	Rect()      // 構造函數,計數器加1  
	{  
		count++;  
	}  
	//Rect(const Rect& r)
	//{
	//	width = r.width;
	//	height = r.height;
	//	count++;
	//}
	~Rect()     // 析構函數,計數器減1  
	{  
		count--;  
	}  
	static int getCount()       // 傳回計數器的值  
	{  
		return count;  
	} 
	friend int get();
private:  
	int width;  
	int height;  
	static int count;       // 一靜态成員做為計數器  
};  

int Rect::count = 0;        // 初始化計數器  
int get()
{
	return Rect::count;//友元函數通過類通路私有靜态成員變量
}
int main()  
{  
	Rect rect1;  
	cout<<"The count of Rect: "<<Rect::getCount()<<endl;//通過類通路公有靜态成員函數,輸出1  

	Rect rect2(rect1);   // 使用rect1複制rect2,此時應該有兩個對象  
	cout<<"The count of Rect: "<<Rect::getCount()<<endl; //輸出1
	cout << get() << endl;//輸出1
	//cout << Rect::count << endl;//不能編譯通過,不能通路私有成員
	system("pause");
	return 0;  
}  
           

3,類和類之間的友元關系不能繼承。

下邊轉載自:

http://blog.csdn.net/shandianling/article/details/7469361

C++ Primer中有如下描述:友元關系不能繼承。基類的友元對派生類的成員沒有特殊通路

權限。如果基類被授予友元關系,則隻有基類具有特殊通路權限,該基類的派生類不能通路授予友元關系的類。

 然而通過實踐發現,VS編譯器并沒有安裝上述描述來處理,下面的規則與上述描述相悖,卻符合VS編譯器的處理規則。

注:有待通過g++編譯器來驗證。

1 友元類的繼承問題

1.1 A類的友元B的派生類C 不能通路A類的private或protect成員變量。但可以通過B提供的接口來通路A。(廢話肯定可以)

[cpp]  view plain  copy

  1. #include <iostream>   
  2. using namespace   std;   
  3. class B;   
  4. class A   
  5. {   
  6.     int a;   
  7. public:   
  8.     A(int x=0) { a=x; }   
  9.     friend class B;   
  10. };   
  11. class B   
  12. {   
  13.     int b;   
  14. public:   
  15.     void fun(A& ob){ cout << ob.a << endl;}   
  16. };   
  17. class C:public B  
  18. {   
  19. public:   
  20.     //void fun2(A& ob){ cout <<ob.a <<endl;}   //派生類新加的函數卻不能通路A,此句會報錯  
  21. };   
  22. void   main()   
  23. {   
  24.     A a(55);   
  25.     C c;   
  26.     c.fun(a); //C是B的派生類   通過基類B的函數fun仍然可以通路   
  27. }   

1.2.  Base的友元可以通過Base的派生類Drived通路Base的private,protect成員變量,但不能通路Drived的private,protect成員變量。(這一點似乎與《C++ primer》裡說的有點沖突)

個人了解:Drived的對象本身就包含Base,Base的友元Frnd自然就可以通路Base的部分。

[cpp]  view plain  copy

  1. #include <iostream>   
  2. using namespace std;   
  3. class Base   
  4. {   
  5.     int m_a;   
  6. public:   
  7.     Base(int x=0){ m_a=x; }   
  8.     friend class Frnd;   
  9. };   
  10. class Drived:public Base   
  11. {   
  12. private:  
  13.     int m_c;  
  14. public:   
  15.     Drived(int x):Base(x){m_c=x;}   
  16. };  
  17. class Frnd   
  18. {  
  19. public:   
  20.     void fun(Base& ob) { cout <<ob.m_a << endl; }   
  21.     void fun2(Drived& ob)   
  22.     {  
  23.         cout << ob.m_a<<endl;  
  24.         //cout <<ob.m_c<<endl; //編譯錯誤  
  25.     }   
  26. };   
  27. int main()   
  28. {   
  29.     Drived d(1);   
  30.     Frnd f;   
  31.     f.fun(d);   
  32.     f.fun2(d);  
  33.     system("pause");  
  34.     return 0;  
  35. }   

3 友元類的傳遞問題

A的友元是B,B的友元是C,那A的友元是C? 不是,友元類不具有傳遞性。

繼續閱讀