虚基类分析
1 #include<iostream> 2 using namespace std; 3 4 class ba{ 5 public: 6 ba(){cout<<"ba"<<endl;} 7 virtual char* vf() const=0; 8 }; 9 10 class bas:public ba{ 11 int k; 12 public: 13 bas(int k){this->k=k;cout<<"bas k="<<k<<endl;} 14 char* vf() const=0; 15 }; 16 class d1:virtual public bas{ 17 public: 18 d1():bas(1){cout<<"d1"<<endl;} 19 char* vf() const { return "d1";} 20 }; 21 class d2:virtual public bas{ 22 public: 23 d2():bas(2){cout<<"d2"<<endl;} 24 char* vf() const { return "d2";} 25 }; 26 class mi:public d1,public d2{ 27 public: 28 mi():bas(3){cout<<"mi"<<endl;} 29 char* vf() const 30 { 31 return d1::vf(); 32 } 33 }; 34 class x:public mi{ 35 public: 36 x():bas(4){cout<<"x"<<endl;} 37 char* vf() const 38 { 39 return "x"; 40 } 41 }; 42 int main() 43 { 44 x xx; 45 return 0; 46 }
以上代码输出结果是
ba
bas k=4
d1
d2
mi
x
由此可见虚继承时最晚派生类必须负责对虚拟基类的初始化。程序先跳至x类构造函数,然后跳至bas类构造函数进行虚拟基类的初始化,而bas是继承自ba的,因此跳至ba先进行ba的初始化。bas初始化完毕后跳至mi构造函数,再先跳至d1构造函数,构造完d1后跳至d2构造函数,然后再跳回mi构造函数构造mi。最后跳回x构造函数完成x的构造。
在d1中,虚基类的指针在虚函数的指针之前。而且因为这些指针的存在,会对类进行内存对齐。如果一个类含有一个虚函数指针,并有一个char类型成员,则这个类大小是8。
如果程序中添加以下四句话:
d1* pd1=&xx; d2* pd2=&xx; bas* pbas=&xx; ba* pba=&xx;
可以发现pba和pbas是的值是一样的,说明虚继承时只存在基类的一份拷贝,而pd1和pd2的值不同,它们应该是d1地址和d2地址到基类地址的偏移值。
若再添加两句话:
x* px=&xx;
mi* pmi=&xx
可以发现px,pmi和pd1的值是一样的。