給出這樣的重複繼承:
(一)、直接繼承,沒有虛函數存在時,書寫如下:
class CA
{……};
class CB:public CA
{……};
class CC:public CA
{……};
class CD:public CB,public: CC
{……};
查看內存佈局如下:
由於B和C都繼承了A,所以在D中重複出現了A中的成員變量,所以當試圖訪問間接基類中的成員變量時,務必要加上作用域。
當把類CA、CB、CC、CD中的函數都寫成虛函數時,依舊還是重複出現,如下圖:
(二)、虛繼承
書寫代碼如下:(將繼承關係改爲虛繼承)
class CA
{
public:
CA(int a=10):ma(a){}
virtual void a(){cout<<"CA::a()"<<" ";}
virtual void aa(){cout<<"CA::aa()"<<" ";}
virtual void aaa(){cout<<"CA::aaa()"<<" ";}
public:
int ma;
};
class CB:virtual public CA
{
public:
CB(int b=20):mb(b){}
virtual void a(){cout<<"CB::a()"<<" ";}
virtual void bb(){cout<<"CB::bb()"<<" ";}
virtual void bbb(){cout<<"CB::bbb()"<<" ";}
protected:
int mb;
};
class CC:virtual public CA
{
public:
CC(int c=30):mc(c){}
virtual void a(){cout<<"CC::a()"<<" ";}
virtual void cc(){cout<<"CC::cc()"<<" ";}
virtual void ccc(){cout<<"CC::ccc()"<<" ";}
protected:
int mc;
};
class CD:public CB,public CC
{
public:
CD(int d):md(d){}
virtual void a(){cout<<"CD::a()"<<" ";}
virtual void bb(){cout<<"CD::bb()"<<" ";}
virtual void ccc(){cout<<"CD::ccc()"<<" ";}
virtual void d(){cout<<"CD::d()"<<" ";}
protected:
int md;
};
對於虛繼承,可以避免重複繼承的作用,打印其內存佈局看看情況:
可以看到在虛繼承下,前面所說的出現兩次的成員變量和成員函數出現了一次,且都放在了內存佈局中的最後面,那怎麼找到呢?就是從圖中看到的vbptr指針,它指向vbtable,vbtable中記錄了兩行數字,據分析,可以做出總結:
根據以上內存佈局,做出打印:
typedef void (*Function)(void);//函數指針
int main()
{
CD d(40);
long** pd=(long**)(&d);//將對象d的地址強轉成爲二級指針。
Function Fun=NULL;
cout<<"[0]"<<" "<<"CB::_vfptr"<<endl;
for(int i=0;i<3;++i)//打印類CB下的vfptr指向的vftable
{
Fun=((Function*)pd[0])[i];
cout<<" "<<"["<<i<<"]"<<" ";
Fun();
cout<<pd[0][i]<<endl;
}
cout<<"[1]"<<" "<<"CB::_vbptr"<<endl;
for(int i=0;i<2;++i)//類CB下vbptr指向的vbtable
{
cout<<" "<<pd[1][i]<<endl;
}
cout<<"[2] CB::mb="<<((long*)(&d))[2]<<endl;//CB::mb
cout<<"[3]"<<" "<<"CC::_vfptr"<<endl;
for(int i=0;i<2;++i)//打印類CC下的vfptr指向的vftable
{
Fun=((Function*)pd[3])[i];
cout<<" "<<"["<<i<<"]"<<" ";
Fun();
cout<<pd[3][i]<<endl;
}
cout<<"[4]"<<" "<<"CC::_vbptr"<<endl;
for(int i=0;i<2;++i)//類CC下vbptr指向的vbtable
{
cout<<" "<<pd[4][i]<<endl;
}
cout<<"[5] CC::mc="<<((long*)(&d))[5]<<endl;//CC::mc
cout<<"[6] CD::md="<<((long*)(&d))[6]<<endl;//CD::md
cout<<"[7] (vtordisp for vbase CA)="<<((long*)(&d))[7]<<endl;
cout<<"[8]"<<" "<<"CA::_vfptr"<<endl;
for(int i=0;i<3;++i)//類CA下vbptr指向的vbtable
{
cout<<" "<<"["<<i<<"]"<<" ";
Fun=((Function*)(pd[8]))[i];
Fun();
cout<<pd[8][i]<<endl;
}
cout<<"[9] CA::ma="<<((long*)(&d))[9]<<endl;//CA::ma
return 0;
}
打印結果如下:
現在就可以畫出內存佈局:
總結:
1、當出現重複繼承時(像這種菱形繼承),會重複出現間接基類的數據,一般爲了避免重複出現,因此採用虛繼承(虛擬繼承)。
2、在虛擬繼承下,編譯器將重複出現的數據放在了這個內存佈局的最後邊(當然有vfptr時還是依據vfptr優先級高放),並且爲了標記它,在每個直接父類的作用域下有了vbptr指針,用來指向存放重複出現數據相對的偏移的vbtable。
3、在查看內存佈局時,看到了下標七號存放着一個0,也就是上面標記的vtordisp for vbase CA,爲什麼是0?不知道爲什麼,也沒查出結果,我猜想是爲了做區分吧。