C++巢狀類的使用及對外部類的訪問許可權
本人能力、精力有限,所言所感都基於自身的實踐和有限的閱讀、查閱,如有錯誤,歡迎拍磚,敬請賜教——部落格園:錢智慧。
先上程式碼:
1 class Outer 2 { 3 public: 4 Outer(){m_outerInt=0;} 5 private: 6 int m_outerInt; 7 public: 8 //內部類定義開始 9 class Inner 10 { 11 public: 12 Inner(){m_innerInt=1;} 13 private: 14 intm_innerInt; 15 public: 16 void DisplayIn(){cout<<m_innerInt<<endl;} 17 } ; 18 //End內部類 19 public: 20 void DisplayOut(){cout<<m_outerInt<<endl;} 21 }; 22 23 int main() 24 { 25 Outer out; 26 Outer::Inner in; 27 out.DisplayOut(); 28 in.DisplayIn();29 30 return 0; 31 }
如上面程式碼所示,這種情況下,外部類與內部類其實聯絡並不大,外部類無非僅僅限定了內部類類名的作用域範圍,完全可以加上Outer限定之後像使用任何其他類一樣來使用內部類,Outer於Inner而言僅僅是一種名稱空間。
提問:上面程式碼中,內部類(Inner)成員函式(比如DisplayIn)如何訪問外部類(Outer)資料成員呢?
答:問這個問題之前,先要明白一個事實:將來你是在一個Inner例項物件上呼叫Inner的成員函式的,而所謂的“訪問外部類資料成員”這種說法是不合理的,“外部類”及任何類,只是程式碼而已,是一種說明,從記憶體的角度來講,程式執行起來之後,程式碼儲存在程式碼區,所以應該問“如何訪問外部類例項的資料成員”,如此,你得先有一個外部類例項(或者例項的指標),然後才能談訪問。
退一步講,如果你不管三七二十一,直接在Inner的DisplayIn方法里加上這樣一行:
1 m_outerInt=10;
然後你編譯、連結也都通過了(事實上這是不可能的),那麼,在main函式中:
1 int main() 2 { 3 Outer::Inner in; 4 in.DisplayIn(); 5 6 return 0; 7 }
如果這樣你都能正常執行,天理何在?DisplayIn中的m_outerInt到底是哪個例項的資料?
所以,為了避免這樣荒唐的事情發生,語法層面就已經使得上述不可能發生:連編譯都不會通過。
提問:把上面程式碼中的Inner設定為Outer的友元類之後,能解決問題嗎?
答:該提問者都不僅犯了第一個提問者的錯誤,還誤解了友元的含義。
友元舉例:
1 class Inner; 2 3 class Outer 4 { 5 public: 6 Outer(){m_outerInt=0;} 7 private: 8 int m_outerInt; 9 public: 10 /*//內部類定義開始 11 class Inner 12 { 13 public: 14 Inner(){m_innerInt=1;} 15 private: 16 int m_innerInt; 17 public: 18 void DisplayIn(){cout<<m_innerInt<<endl;} 19 } ; 20 //End內部類*/ 21 public: 22 void DisplayOut(){cout<<m_outerInt<<endl;} 23 friend Inner; 24 }; 25 class Inner 26 { 27 public: 28 Inner(){m_innerInt=1;} 29 private: 30 int m_innerInt; 31 public: 32 void DisplayIn(){cout<<m_innerInt<<endl;} 33 //友元影響的函式 34 void TestFriend(Outer out) 35 { 36 cout<<"Good Friend:"<<out.m_outerInt<<endl; 37 } 38 } ; 39 40 int main() 41 { 42 Outer out; 43 out.DisplayOut(); 44 Inner in; 45 in.DisplayIn(); 46 in.TestFriend(out); 47 return 0; 48 }
內部類如果想達到友元訪問效果(直接通過例項或者例項指標來訪問例項的非公有成員),是不需要另外再宣告為friend的(預設可以訪問外部類的友元類),原因不言自明:都已經是自己人了。
提問:內部類例項(作為外部類的資料成員)如何訪問外部類例項的成員呢?
見如下程式碼:
1 #include <iostream> 2 #define METHOD_PROLOGUE(theClass, localClass) \ 3 theClass* pThis = ((theClass*)((char*)(this) - \ 4 offsetof(theClass, m_x##localClass))); \ 5 6 using namespace std; 7 8 class Outer 9 { 10 public: 11 Outer(){m_outerInt=0;} 12 private: 13 int m_outerInt; 14 public: 15 //內部類定義開始 16 class Inner 17 { 18 public: 19 Inner(){m_innerInt=1;} 20 private: 21 int m_innerInt; 22 public: 23 void DisplayIn(){cout<<m_innerInt<<endl;} 24 // 在此函式中訪問外部類例項資料 25 void setOut() 26 { 27 METHOD_PROLOGUE(Outer,Inner); 28 pThis->m_outerInt=10; 29 } 30 } m_xInner; 31 //End內部類 32 public: 33 void DisplayOut(){cout<<m_outerInt<<endl;} 34 }; 35 36 int main() 37 { 38 Outer out; 39 out.DisplayOut(); 40 out.m_xInner.setOut(); 41 out.DisplayOut(); 42 return 0; 43 }
看main函式:程式執行完main函式第一句後,記憶體中便有了一個數據塊,它儲存著out的資料,而m_xInner也在資料塊中,當然,&out和this指標(外部類)都指向該記憶體塊的起始位置,而內部類程式碼中的this指標當然就指向m_xInner的起始記憶體了,offsetof(theClass, m_x##localClass)獲得的便是m_xInner在該記憶體塊中與該記憶體塊起始地址(這正是out的地址)的距離(偏移),即內部類this-外部類this的差值(以位元組為單位)這樣,用內部類this減去其自身的偏移,便可得到pThis。有了out的地址,基本上可以對其為所欲為了,至於為何要有char*強轉,可以go to definition of offsetof,可以看到其實現中有個關於char的轉換。
提問:巢狀類為什麼可以訪問外圍類的私有靜態函式而不可訪問非靜態函式?
在一個類中定義的類稱為巢狀類,定義巢狀類的類稱為外圍類。
定義巢狀類的目的在於隱藏類名,減少全域性的識別符號,從而限制使用者能否使用該類建立物件。這樣可以提高類的抽象能力,並且強調了兩個類(外圍類和巢狀類)之間的主從關係。下面是一個巢狀類的例子:
class A
{
public :
class B
{
public :
…
private :
…
};
void f();
private :
|