文章摘自 https://www.cnblogs.com/yvictoryr/p/3773733.html ,紅色字體爲我認爲原博客錯誤的地方。
虛繼承是爲了解決多重繼承中的問題而出現的。要理解虛繼承的實現機制。
首先看一般繼承:
class A { char k[3]; public: virtual void aa() { }; }; class B:public A { char j[3]; public: virtual void bb() { }; }; class C:public B { char i[3]; public: virtual void cc() { }; }; int main() { cout<<sizeof(A)<<endl; cout<<sizeof(B)<<endl; cout<<sizeof(C)<<endl; return 0; }
得到的答案是:8 12 16
分析如下:
1、 對於類A,有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量char k[3],則根據數據對齊原則,可以得到sizeof(A)的大小爲8;
2、 對於類B,也有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量j[3],但是類B是繼承自類A的,是一般繼承,不是虛繼承,所以可以得到sizeof(B)的大小爲: 4(指向虛函數的虛指針)+4(自己的數據成員)+4(父類A的數據成員)=12;
3、 對於類C,也有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量i[3],同樣的,類C是繼承自類B的,是一般繼承,不是虛繼承,所以可以得到sizeof(C)的大小爲4(指向虛函數的虛指針)+4(自己的數據成員)+8(父類的數據成員)=16;
以上表格的虛函數指針應該位於每個類的起始地址,而不是最後。
再來看一下虛繼承的情況:
class A { char k[3]; public: virtual void aa() { }; }; class B:public virtual A { char j[3]; public: virtual void bb() { }; }; class C:public virtual B { char i[3]; public: virtual void cc() { }; }; int main() { cout<<sizeof(A)<<endl; cout<<sizeof(B)<<endl; cout<<sizeof(C)<<endl; return 0; }
得到的答案是:8 20 32
分析如下:
1、對於類A,有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量k[3],則根據數據對齊原則,可以得到sizeof(A)的大小爲8;
2、對於類B,也有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量j[3],但是類B是繼承自類A的,而且是虛繼承,虛繼承的實現是通過一個虛基類指針列表,比如vptr_B_A指向虛基類,同時還包含了父類的所有內容,所以可以得到sizeof(B)的大小爲:4(指向自己虛函數的虛指針)+4(自己的數據成員)+4(指向虛基類的指針vptr_B_A)+8(父類A的內容大小)=20;
3、對於類C,也有一個虛函數,需要一個虛函數表vtbl來記錄函數的入口地址,每個地址一個虛指針,指針的大小爲4,類中還有一個成員變量i[3],同樣的,類C是繼承自類B的,而且是虛繼承,虛繼承的實現是通過一個虛基類指針列表,比如vptr_C_B指向虛基類,同時還包含了父類的所有內容,所以可以得到sizeof(C)的大小爲:4(指向自己虛函數的虛指針)+4(自己的數據成員)+4(指向虛基類的指針(vptr_C_B)+20(父類B的內容大小)=32;
class A { char k[3]; public: virtual void aa() { }; }; class B:public virtual A { char j[3]; public: virtual void bb() { }; }; class C:public virtual A { char i[3]; public: virtual void cc() { }; }; class D: public B,public C { char n[3]; public: virtual void dd() { }; }; int main() { cout<<sizeof(A)<<endl; cout<<sizeof(B)<<endl; cout<<sizeof(C)<<endl; cout<<sizeof(D)<<endl; return 0; }
得到的結果爲:8 20 20 36
分析如下:
1、類sizeof(A,B,C),從前面的分析可以得到結果爲8 20 20;
2、sizeof(D) =
12【父類B的內容,包括指向虛函數的虛指針(4,合併類D的虛函數)、虛基類指針列表vptr_B_A(4)、數據成員(4)】
+ 12【父類C的內容,包括指向虛函數的虛指針(4)、虛基類指針列表vptr_C_A(4)、數據成員(4)】
+ 4(類D的的數據成員)+ 4(類A虛函數的虛指針)+ 4(類A的數據成員)= 36。
可見,類D中只包含了類A的一份副本,虛繼承很好地解決了多重繼承中的二義性問題。