程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 虛繼承的內存模型分析

虛繼承的內存模型分析

編輯:C++入門知識

 

這裡就先從以下幾個點進行說明吧:

  •  虛繼承和虛基類
  • vs2010下的cl命令
  • 內存模型

     虛繼承和虛基類

     虛繼承:在繼承定義中包含了virtual關鍵字的繼承關系;
     虛基類:在虛繼承體系中的通過virtual繼承而來的基類,需要注意的是:class CSubClass : public virtual CBase {}; 其中CBase稱之為CSubClass的虛基類,而不是說CBase就是個虛基類,因為CBase還可以不不是虛繼承體系中的基類。

     vs2010下的cl命令

     微軟的VS2010提供了一個新的選項,給用戶顯示C++對象在內存中的布局。這個選項就是:

[cpp] view plaincopyprint?
  1. /d1reportSingleClassLayout  

具體使用方法如下,在寫好相應的cpp文件之後,需要啟動VS2010的命令行工具“Visual Studio 2010Command Prompt”,切換到cpp文件所在目錄之後,輸入如下的命令:

[cpp] view plaincopyprint?
  1. cl [filename].cpp /d1reportSingleClassLayout[className]  

cl當然就是MS的編譯器;[filename].cpp就是你所想要查看的class所在的cpp文件(class定義在頭文件也沒關系,還是只要編譯cpp文件即可);而你需要在最後加上[className],也就是你需要查看的class的類名。

【舉例】test.cpp文件代碼如下:

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Base  
  5. {  
  6. public:  
  7.     int a;  
  8.     virtual void fcn() {};  
  9. };  
  10.   
  11. class Derived : public Base  
  12. {  
  13. public:  
  14.     virtual void fcn2() {};  
  15. private:  
  16.     int d;  
  17.     void fcn3() { }  
  18. };  
  19.   
  20. int main()   
  21. {  
  22. }  

查看Derived這個類的對象在內存中的布局,那麼就可以用下面的命令行:

[cpp] view plaincopyprint?
  1. cl Test.cpp /d1reportSingleClassLayoutDerived  

結果顯示如下:


       可以看到class Derived的對象的內存布局,在派生類對象的開始包含了基類Base的對象,其中有一個虛表指針,指向的就是下面的 Derived::$vftable@ (virtual function table),表中包含了Derived類中所有的虛函數。

      內存模型

     在這一小節裡面我主要從他面試的幾個題目中來談談虛繼承的內存模型。

 

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3.     virtual void a()  
  4.     {  
  5.     }  
  6. };  
  7. class A1  
  8. {  
  9.     virtual void a()  
  10.     {  
  11.     }  
  12. };  
  13.   
  14. class B : public A , virtual public A1  
  15. {  
  16. };  
  17. void main()  
  18. {  
  19.     cout<<"sizeof A: "<<sizeof(A)<<endl;  
  20.     cout<<"sizeof A1: "<<sizeof(A1)<<endl;  
  21.     cout<<"sizeof B: "<<sizeof(B)<<endl;     //   
  22. }  

輸出結果是:

在命令行中輸入:

[cpp] view plaincopyprint?
  1. cl test.cpp /d1reportSingleClassLayoutB  

         從這個內存布局就可以看出來class A、class A1和ClassB的大小,本身class A的大小應該是1bytes的內存定位大小加上虛函數指針4bytes因為有了虛函數指針後1bytes的占 位就可以取消了。所以A的大小就是4bytes,同理Class A1。對於Class B它主要是從class A和class A1(虛繼承)而來,所以B裡面包含有一個A和A1同時因為是虛繼承所以就有一個指向虛基類(A1)的vbptr指針。這裡為了方便我做個圖直觀一點:


        所以說class B的大小是12bytes

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3.     int a;  
  4. };  
  5. class B  
  6. {  
  7.     int b;  
  8. };  
  9.   
  10. class C  
  11. {  
  12. };  
  13. class D  
  14. {  
  15. };  
  16. class E: public virtual A , public virtual B , public virtual C , public virtual D  
  17. {  
  18. };  
  19. void main()  
  20. {  
  21.     cout<<"sizeof E: "<<sizeof(E)<<endl;      //   
  22. }  

     

        上面代碼二輸出的結果:


class E的大小是不是有點怪,在命令行中輸入:

[cpp] view plaincopyprint?
  1. cl test.cpp /d1reportSingleClassLayoutE  

           如前面黑色字體標注一樣,因為classE是多重虛繼承,所以在內存中的布局是分為固定局部和共享局部,固定局部的大小就int a,b 所以是8 bytes。我的上一篇博文(VC++對象布局的奧秘:虛函數、多繼承、虛擬繼承)對這個內存某型有個大概的講解,在這就不多言了。

[cpp] view plaincopyprint?
  1. E::$vbtable@:  
  2. 1>   0   | 0  
  3. 1>   1   | 4 (Ed(E+0)A)  
  4. 1>   2   | 8 (Ed(E+0)B)  
  5. 1>   3   | 12 (Ed(E+0)C)  
  6. 1>   4   | 16 (Ed(E+0)D)  

這個地方的4表示E的vbtable與虛基類A首地址的偏移量,同理,8,12,16這個就不用我說了。既然這都給出了vbtable域虛基類的地址偏移量了,說明在E對象的內存中還是存在分配的空間。


          從vs2010的內存來看的確也是分配了,通過前面的vbtable的偏移來看對象的大小就是16bytes(a是class A的首地址,b是classB的首地址)。有人會問為什麼不是20bytes?上面的內存分配偏移是16就說明有20bytes。偏移16,剛好在變量b 的下面分配4bytes的C(這說的不准確,不過好明白)這樣偏移是16的地方就是D,vbtable現在可以正常定位,Class D為空,就沒有必要再分配4bytes的空間,所以sizeof E的大小應該是:vbtable指針(4bytes) +固定局部(a,b工8bytes)+ C 的4bytes(這有點不好明白,不過下面我還有例子,加深理解)。

 

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. };  
  4. class B  
  5. {  
  6.     int b;  
  7. };  
  8.   
  9. class C  
  10. {  
  11. };  
  12. class D  
  13. {  
  14. };  
  15. class E: public virtual A , public virtual B , public virtual C , public virtual D  
  16. {  
  17. };  
  18. void main()  
  19. {  
  20.    cout<<"sizeof A: "<<sizeof(A)<<endl;  
  21.    cout<<"sizeof B: "<<sizeof(B)<<endl;  
  22.    cout<<"sizeof C: "<<sizeof(C)<<endl;  
  23.    cout<<"sizeof D: "<<sizeof(D)<<endl;  
  24.    cout<<"sizeof E: "<<sizeof(E)<<endl;      //   
  25. }  

上面代碼二輸出的結果:

這回感覺還是很奇怪吧,看看內存布局吧。

        首先,看看vbtable把,是不是很奇怪,為什麼A,B的偏移都是4,ClassA本身就是空,剛好ClassB有一個成員所以肯定需要給classB 分配內存的,所以這樣就可以找到A的偏移,Class C和Class D就沒辦法了沒辦法計算偏移,所以就給ClassC分配了內存,ClassC有了內存這樣D就可以的偏移也就出來了,這時候就有人問為什麼ClassD的 偏移為什麼不是8呢(說實話我也沒搞懂,我的猜想是對象本身為空,但是為了內存對齊所以就是4bytes。希望懂的大神給以指點,我通過調試多個例子,得 出來的,下面的例子就就更好的說明了這點),

[cpp] view plaincopyprint?
  1. class A  
  2. {  
  3. };  
  4. class B  
  5. {     
  6. };  
  7.   
  8. class C  
  9. {  
  10. };  
  11. class D  
  12. {  
  13. };  
  14. class E: public virtual A , public virtual B , public virtual C , public virtual D  
  15. {  
  16. };  
  17. void main()  
  18. {  
  19.     E ee;  
  20.     cout<<"sizeof A: "<<sizeof(A)<<endl;  
  21.     cout<<"sizeof B: "<<sizeof(B)<<endl;  
  22.     cout<<"sizeof C: "<<sizeof(C)<<endl;  
  23.     cout<<"sizeof D: "<<sizeof(D)<<endl;  
  24.     cout<<"sizeof E: "<<sizeof(E)<<endl;      //   
  25. }  

輸出結果是:

再看看內存模型吧


         從vbtable來看,裡面存儲的偏移class E的大小默認固定布局是4bytes,共享布局裡面classA、class B和classC的大小是12bytes。所以就是16bytes。

來源:http://blog.csdn.net/wangqiulin123456/article/details/8059536

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved