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         int m_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 }
View Code

如上面代碼所示,這種情況下,外部類與內部類其實聯系並不大,外部類無非僅僅限定了內部類類名的作用域范圍,完全可以加上Outer限定之后像使用任何其他類一樣來使用內部類,Outer於Inner而言僅僅是一種命名空間。

提問:上面代碼中,內部類(Inner)成員函數(比如DisplayIn)如何訪問外部類(Outer)數據成員呢?

答:問這個問題之前,先要明白一個事實:將來你是在一個Inner實例對象上調用Inner的成員函數的,而所謂的“訪問外部類數據成員”這種說法是不合理的,“外部類”及任何類,只是代碼而已,是一種說明,從內存的角度來講,程序運行起來之后,代碼存儲在代碼區,所以應該問“如何訪問外部類實例的數據成員”,如此,你得先有一個外部類實例(或者實例的指針),然后才能談訪問。

退一步講,如果你不管三七二十一,直接在Inner的DisplayIn方法里加上這樣一行:

1 m_outerInt=10;
View Code

然后你編譯、鏈接也都通過了(事實上這是不可能的),那么,在main函數中:

1 int main()
2 {
3     Outer::Inner in;
4     in.DisplayIn();
5 
6     return 0;
7 }
View Code

如果這樣你都能正常運行,天理何在?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 }
View Code

內部類如果想達到友元訪問效果(直接通過實例或者實例指針來訪問實例的非公有成員),是不需要另外再聲明為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 }
View Code

看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的轉換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM