给出这样重复继承:
(一)、直接继承,没有虚函数存在时,书写如下:
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?不知道为什么,也没查出结果,我猜想是为了做区分吧。