c++ 虛繼承詳解

鴨脖發表於2013-05-03

#include <iostream>
using namespace std;
class A
{
 virtual void f(){};
};
class B : public virtual  A{
  
 virtual void f(){};
};
class C: public virtual  A{
 virtual void f(){};
 virtual void t(){};
};
int main() 

   cout<<sizeof(B)<<endl;
  cout<<sizeof(C)<<endl;

此題在vc6.0下結果是 8 12。

回顧一下虛繼承的概念:虛繼承主要解決在多重繼承中的菱形繼承問題,也就是說 B和C類同時繼承了A類,然後D類繼承了B,C類,那麼D類的虛表就會有重複的函式指標,虛繼承就不會了……。實現方法是,在子類中儲存一個父類(虛繼承類)的實體,同時儲存一個指標指向這個實體。指標+實體都是屬於子類,所以sizeof會將兩者也算在內。

cout<<sizeof(B)<<endl; 結果是8原因是 sizeof(A)加上 指向A實體的指標。注意此時沒有屬於B的虛指標。也就是說B沒有自己的虛擬函式。

 cout<<sizeof(C)<<endl; C B區別在於C中有一個屬於自己的虛擬函式,所以加上了一個虛指標的大小 所有為12



最後我們來看一個完整的例子以及記憶體結構佈局。圖後有相關程式碼。

 

 VS2003虛繼承記憶體結構圖

 有了之前的那個思想,我來講解下 各個結構體的 sizeof();

程式碼如下:

struct A

{

    A(int v=100):X(v){};

    virtual void foo(void){}

    int X;

};

 A:很簡單 一個虛表指標 +一個 X 一共是8byte

struct B :virtual public A

{

    B(int v=10):Y(v),A(100){};

    virtual void fooB(void){}

    int Y;

};

 B:虛繼承,OK 那就是 sizeof(A)+一個指向虛基類的指標4byte+判斷B中的虛擬函式是不是從A繼承的,如果是則這一部分是0,如果不是則還要再加4byte 存放 虛表 那麼 B一共就是20byte。

struct C : virtual public A

{

    C(int v=20):Z(v),A(100){}

    virtual void fooC(void){}

    int Z;

};

 C的分析同A

 

struct D : public B, public C

{

    D(int v =40):B(10),C(20),A(100),L(v){}

    virtual void fooD(void){}

    int L;

};

 D:公共繼承B,C,那麼 直接 sizeof(b)+sizeof(C)+自己的一個虛指標-因為B,C都是虛繼承A,那麼B和C中關於A的指標只要儲存一個,所以要減去4個位元組,那麼D最後一共就是40byte OK

 

int _tmain(int argc, _TCHAR* argv[])

{

   

    A a;

    int *ptr;

    ptr = (int*)&a;

    cout << ptr << " sizeof = " << sizeof(a) <<endl;

    for(int i=0;i<sizeof(A)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

 

    cout << "--------------------------------------" <<endl;

 

    B b;

    ptr = (int*)&b;

    cout <<"addr:" << ptr << " sizeof = " << sizeof(b) <<endl;

    for(int i=0;i<sizeof(B)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

 

    cout << "--------------------------------------" <<endl;

   

    D d;

    ptr = (int*)&d;

    cout <<"addr:" << ptr << " sizeof = " << sizeof(d) <<endl;

    for(int i=0;i<sizeof(D)/sizeof(int);i++)

    {

        if(ptr[i] < 10000)

        {

             cout << dec << ptr[i]<<endl;

        }

        else cout << hex << ptr[i] <<" = " << hex << * ((int*)(ptr[i])) <<endl;

    }

    return 0;

}


最後一段話很重要:那就是 各個編譯器執行的關於虛繼承的結果不一樣,很簡單,他們處理虛表的機制不一樣,但是有一點可以肯定的是,虛繼承就是為了解決菱形繼承中,B,C都繼承了A,D繼承了B,C,那麼D關於 A的引用只有一次,而不是 普通繼承的 對於A引用了兩次……

所以上面分析的都是浮雲 沒用的東西

相關文章