繼承是C++做爲OOD程序設計語言的三大特徵(封裝,繼承,多態)之一,單一非多態繼承是比較好理解的,本文主要講解GCC環境下的多重繼承和虛擬繼承的對象內存佈局。ios
1、多重繼承函數
先看幾個類的定義:佈局
class Top { public: int a; }; class Left : public Top { public: int b; }; class Right : public Top { public: int c; }; class Bottom : public Left, public Right { public: int d; };
不難想象,Left和Right類的內存佈局以下圖所示:
咱們以下進行驗證:spa
Left *left = new Left(); Top *top = left; cout << left << '\t' << top << endl;//輸出:0x902c008 0x902c008 Right *right = new Right(); top = right; cout << right << '\t' << top << endl;//輸出:0x902c018 0x902c018
從輸出結果能夠看出,父類指針top指向子類對象left和right的起始地址,與上述內存佈局吻合。
在非虛擬多重繼承的狀況下,子類的內存佈局是什麼樣子的呢?以下所示:設計
能夠看出,Bottom類因爲繼承了Left和Right,而Left和Right又分別繼承了Top。所以,Bottom包含了Top兩次!指針
下面進行驗證:code
Bottom *bottom = new Bottom();
// top = bottom; //error: ‘Top’ is an ambiguous base of ‘Bottom’
top = (Left *)bottom;
left = bottom; cout << bottom << '\t' << top << '\t' << left << endl;//輸出:0x9930028 0x9930028 0x9930028 top = (Right *)bottom; right = bottom; cout << bottom << '\t' << top << '\t' << right << endl;//輸出:0x9930028 0x9930030 0x9930030
好了,到這裏講完了非虛擬繼承下的多重繼承的內存佈局狀況,相信你們應該有一個比較清晰的認識了。最重要的一點是: 多重繼承時,父類共同繼承的祖父類會在子類中有多份存在。
2、虛擬繼承對象
平時討論的最多的是虛函數,不多涉及到虛擬繼承的狀況。那麼,虛擬繼承究竟是一個什麼概念呢?繼承
先來看一個例子: 內存
#include <iostream> using namespace std; class Father { public: int a; }; class Child : virtual public Father { public: int b; }; int main() { cout << sizeof(Father) << '\t' << sizeof(Child) << endl;//輸出:4 12 Child child; cout << &child << '\t' << &child.b << '\t' << &child.a << endl;//輸出:0xbfc08124 0xbfc08128 0xbfc0812c return 0; }
對,你沒有看錯,類的大小輸出不是4 8,而是4 12。虛擬繼承時,編譯器會在子類中安插上一個虛表指針。
從輸出的對象成員地址來看,咱們能夠獲得Child類的以下內存佈局:
如今咱們對多重繼承的例子進行改造:
class Top { public: int a; }; class Left : virtual public Top { public: int b; }; class Right : virtual public Top { public: int c; }; class Bottom : public Left, public Right { public: int d; };
把Left和Right改爲了虛擬繼承Top。
從上面驗證簡單虛擬繼承時,編譯器安插虛表指針的例子,咱們能夠想象出此時Bottom類的對象內存佈局以下:
對,你沒有看錯!虛擬繼承時,子類只有父類共同繼承的祖父類的一份存在。這其實也就是虛擬繼承的最大用途。此時,Top,Left,Right和Bottom對象的大小分別爲:4 ,12 ,12 ,24。
既然有虛表指針了,那麼Bottom的虛表是什麼樣的呢?請看:
有了虛表,內存佈局狀況一目瞭然。下面咱們進行驗證:
Bottom *bottom = new Bottom(); top = bottom; cout << bottom << '\t' << top << endl;//輸出:0x9fa5028 0x9fa503c Left *left = bottom; cout << bottom << '\t' << left << endl;//輸出:0x9fa5028 0x9fa5028 Right *right = bottom; cout << bottom << '\t' << right << endl;//輸出:0x9fa5028 0x9fa5030
根據輸出結果,咱們能夠知道指針的指向狀況:
因爲引入了虛指針和虛表,left指針和right指針能夠根據虛表提供的偏移量信息,輕鬆訪問到Top::a。
到此爲止,已經討論清楚了多重繼承和虛擬繼承下的對象內存佈局狀況。總結下:非虛擬多重繼承時,子類會有父類
共同繼承祖父類的多份存在;虛擬繼承時,子類會被安插一個虛擬指針;多重虛擬繼承時,子類只有父類共同繼承祖父類的一
份存在。經過父類的虛擬指針,能夠正確地訪問祖父類中的成員。
參考文獻:
1.http://www.tbdata.org/archives/878
2. 《深度探索C++對象模型》