虛繼承的時候在子類的物件中會多出一個叫虛類指標的大小,有的資料說這個指標指向的記憶體裡面包含了該子類的偏移量和到基類的距離。但是我跟蹤過這段記憶體,發現裡面的資料沒有規律,也找不到更多的支撐材料,權且先知道子類的物件裡面會有這麼一個東西吧。

先總結虛擬繼承中比較特殊的地方,希望能夠對大家有所幫助:

虛繼承時子類的虛擬函式不再是新增到父類部分的虛表中,而在普通的繼承中確實直接新增到父類的虛表中,這就意味著如果虛繼承中子類父類都有各自的虛擬函式,在子類裡面就會多出一個虛表指標,而普通的繼承卻不會這樣。程式碼說明:

class B
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p+=1;
 //跳過C的虛類指標
 p+=1;
 //跳過C的陣列地址
 p+=1;

 memcpy(&p2,p,4);//定位到B的虛表地址
 memcpy(&PtrFun,p2,4);  //呼叫B的虛擬函式                 
 PtrFun();

 cout<<sizeof(c)<<endl;
}

普通繼承中子類中則不會多出虛類指標,子類也不會有自己單獨的虛擬函式列表,子類的虛擬函式會被嵌到基類部分的虛擬函式表的後面,程式碼如下:

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p2+=1;
 memcpy(&PtrFun,p2,4);
 PtrFun();

 cout<<sizeof(c)<<endl;
}

從中我們不難發現虛擬繼承父類子類的記憶體排列方式也有很大的差別,普通繼承中父類在前,子類在後;虛擬繼承中先是子類部分的記憶體,接著再是父類的記憶體。對於虛擬繼承中的多次繼承就更奇葩了。

附上程式碼,大家應該很容易看明白其中的記憶體是如何分佈的:

#include <iostream>
using namespace std;

class A
{
public:
 char a[3];
public:
 virtual void ba()
 {
  cout<<"A"<<endl;
 }
};

class B:public virtual  A
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;
 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p+=1;
 //跳過C的虛類指標
 p+=1;
 //跳過C的陣列地址
 p+=1;

 memcpy(&p2,p,4);//我覺得應該是到B的地址了,但是確實到A的地址
 memcpy(&PtrFun,p2,4);                    //大概是將A放到了前面吧
 PtrFun();//呼叫A的虛擬函式,表明此時的p所儲存的是A的虛擬函式表的位置

 p+=1;
 //跳過A的陣列地址
 p+=1;

 memcpy(&p2,p,4);
 memcpy(&PtrFun,p2,4);
    PtrFun();//呼叫B的虛擬函式,表明此時的p所儲存的是B的虛表地址

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 c.a[0]=7;
 c.a[1]=8;
 c.a[2]=9;
}

沒錯 C的記憶體是在前面,但是後面緊接著的不是B的記憶體而是C的記憶體,至於為什麼會這樣分配,這個我也不知道,希望知道的人可以指點一下。(普通繼承下是 C B A)

.