跟我一塊兒學習C++虛函數--第四篇

      在前一篇,咱們討論了在多繼承情況下,具備虛函數的類的內存佈局狀況。本篇將進一步探索在多重繼承,即具備重複繼承的狀況下的內存佈局狀況。在閱讀本篇和下一篇以前,建議先閱讀本博客另外一篇博文《淺析GCC下C++多重繼承 & 虛擬繼承的對象內存佈局》。ios

       先說一點題外話,細心的讀者可能會發現,咱們在探索不一樣狀況下類的內存佈局時,老是先經過查看類的大小以及其中各個成員變量的地址來進行分析,而後再具體定位某一位置的值。從最原始的內存中的對象分佈,咱們能夠得到最深刻最有效的理解。函數

      OK,請看例子:佈局

#include <iostream>
using namespace std;

class Top
{
public:
    virtual void x(){cout << "top x" << endl;}
    virtual void print0(){cout << "top print" << endl;}
public:
    int a;
};

class Left:public Top
{
public:
    virtual void y(){cout << "left y" << endl;}
    virtual void print1(){cout << "left print" << endl;}
public:
    int b;
};

class Right:public Top
{
public:
    virtual void z(){cout << "right z" << endl;}
    virtual void print2(){cout << "right print" << endl;}
public:
    int c;
};

class Bottom : public Left, public Right
{
public:
    virtual void y(){cout << "bottom y" << endl;}
    virtual void z(){cout << "bottom z" << endl;}
    virtual void print3(){cout << "bottom print" << endl;}
public:
    int d;
};

 int main()
 {
    /*first part*/
    cout << sizeof(Top) << "\t" << sizeof(Left) << "\t" << sizeof(Right) << "\t" << sizeof(Bottom) << endl;
    //輸出:8       12      12      28
    Bottom *b = new Bottom();
    cout << b << " " << &b->Left::a << " " << &b->b << " " << &b->Right::a << " " << &b->c << " " << &b->d << endl;
  //輸出:0x8c0f008 0x8c0f00c 0x8c0f010 0x8c0f018 0x8c0f01c 0x8c0f020
	
    /*second part*/
    typedef void (*Func)(void);
    Func pFunc;
    pFunc = (Func)*((int *)*(int *)(b));
    pFunc();//輸出:top x
    pFunc = (Func)*((int *)*(int *)(b)+1);
    pFunc();//輸出:top print
    pFunc = (Func)*((int *)*(int *)(b)+2);
    pFunc();//輸出:bottom y
    pFunc = (Func)*((int *)*(int *)(b)+3);
    pFunc();//輸出:left print
    pFunc = (Func)*((int *)*(int *)(b)+4);
    pFunc();//輸出:bottom z
    pFunc = (Func)*((int *)*(int *)(b)+5);
    pFunc();//輸出:bottom print
    //pFunc = (Func)*((int *)*(int *)(b)+6);
    //pFunc();//段錯誤

    /*third part*/
    pFunc = (Func)*((int *)*((int *)(b)+3));
    pFunc();//輸出:top x
    pFunc = (Func)*((int *)*((int *)(b)+3)+1);
    pFunc();//輸出:top print
    pFunc = (Func)*((int *)*((int *)(b)+3)+2);
    pFunc();//輸出:bottom z
    pFunc = (Func)*((int *)*((int *)(b)+3)+3);
    pFunc();//輸出:right print
    //pFunc = (Func)*((int *)*((int *)(b)+3)+4);
    //pFunc();//段錯誤

    delete b;
    return 0;
 }

 對於上面的例子,咱們分爲三部分進行講解。

第一部分:多重繼承狀況下,對象自己(除虛函數表外)的內存佈局。spa

        從代碼中first part的輸出狀況來看,Top、Left和Right的大小很容易理解,至於Bottom類的大小,若是你看過參考文獻一,那麼也很容易理解。Bottom類包含了兩次Top類中的a成員變量,所以總共有5個int成員變量,爲20字節。再加上兩個虛指針,即爲28字節。.net

       從輸出的Bottom對象中成員變量地址狀況,咱們能夠用以下圖來表示內存佈局:指針

        

        簡單地說,多重繼承時,子類會有多個祖父類的存在。code

第二部分:多重繼承狀況下,主要虛函數表的內存佈局。orm

        從代碼的second part的輸出狀況來看,咱們能夠用下圖來表示主要虛函數表的內存佈局:對象

       

第三部分:多重繼承狀況下,次要虛函數表的內存佈局。blog

        從代碼的third part的輸出狀況來看,咱們能夠用下圖來表示次要虛函數表的內存佈局:

       

        

         對比前一篇多繼承狀況下類的內存佈局,咱們能夠發現:在多重繼承狀況下,不只祖父類的成員變量在子類中會有多份存在,祖父類的虛函數一樣會在子類的虛函數表中有多份存在,分別位於主要虛函數表和次要虛函數表中。

 

參考文獻:

          1.《淺析GCC下C++多重繼承 & 虛擬繼承的對象內存佈局

          2. 《深度探索C++對象模型》

相關文章
相關標籤/搜索