菱形继承和虚继承、对象模型和虚基表
1.菱形繼承(鉆石繼承):兩個子類繼承同一父類,而又有子類同時繼承這兩個子類。例如B,C兩個類同時繼承A,但是又有一個D類同時繼承B,C類。
2.菱形繼承的對象模型
class A { public:int _a; };class B :public A { public:int _b; }; class C :public A { public:int _c; }; class D :public B,public C { public:int _d; };上述的代碼對應的菱形繼承的對象模型如下圖所示
3.菱形繼承所帶來的問題
(1)由上圖的對象模型可以看出,D的對象中有兩份A成員,造成了數據的冗余。使用sizeof去驗證,可以發現d的大小為20個字節。
(2)會造成基類子對象重復,即二義性。造成訪問基類的成員不明確。
4.解決菱形繼承所帶來的問題
(1)要解決二義性很簡單,可以顯示的指定訪問哪個父類的成員,但是這個還是不能從根本上解決這個問題。如:
(2)要從本質上解決上述問題,需要采用虛繼承:虛繼承是一種機制,類通過虛繼承指出它希望共享虛基類的狀態。對給定的虛基類,無論該類在派生層次中作為虛基類出現多少次,只繼承一個共享的基類子對象,共享基類子對象稱為虛基類。虛基類用virtual聲明繼承關系就行了。這樣一來,D就只有A的一份拷貝。
如:
采用虛繼承和顯示指定訪問父類成員,對對象所產生的的影響有什么不同呢?顯示指定訪問并不能起到一改全改的作用,那么就會造成一個對象d有兩個不相同的成員_a,明顯是不符合常規的。
5.深入理解虛繼承
現在來了解多出來的四個字節究竟是什么?首先我們通過調試——>內存查看對象d的地址的數據,可以看到如下現象:
(1)(2)中存放的數據究竟是用來表示什么的呢?再將d._a分別賦值成為0和4,通過內存窗口觀察d的地址的數據
由圖可以看到末排的地址存放的數據剛好是d._a的值的大小,那么多出來的第一排和第三排的數據看起來似乎是兩個地址,輸入地址查看一下數據
明顯可以看到兩個地址存放的數據是每個對象相對于基類的成員的偏移量,對應的每個地址所代表的意義如下:
這樣也就可以解釋為什么d的大小為24個字節因為虛繼承引入了間接性指針
6.在虛繼承的前提下,重新建立對象模型
7.在菱形繼承且為虛繼承的前提下,討論虛基表為什么首先不存偏移量,而是在存偏移量之前預留了一個0x0000 0000的位置呢?
(1)首先,設置一個菱形繼承,且為虛繼承,每個子類既重寫了父類的虛函數,還擁有自己的虛函數
用菱形繼承的模型圖來表示關系就是:
通過實例化出對象d,并查看內存可以看到對象布局,可以看到有五個類似地址的部分,一一通過內存窗口查看內容,發現三張為虛表(存放虛函數),兩張為虛基表(存放偏移量的),在下圖中用不同的顏色標注出來了:
通過內存窗口查看各個虛基表的地址:
可以看到虛基表的開始預留一個位置是用來存放虛基表和虛表的地址之差的。
內存分布模型:
那么可以看到實例化出來的對象d的對象模型為:
至于虛表存放的虛函數順序是怎么存放的:
http://blog.csdn.net/skyroben/article/details/68192874
總結
以上是生活随笔為你收集整理的菱形继承和虚继承、对象模型和虚基表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 富瀚微的芯片哪里代工 带你了解
- 下一篇: 京东白条还了还能借出来吗