虚拟继承的实现机制在不同的编译器下方法是不同的,因此造成不同编译器下类对象的大小有差异。先上代码:
8 #include<iostream>
9 using namespace std;
10 class A{
11 public:
12 virtual void display1(){}
13 private:
14 // int x;
15 };
16 class B: public virtual A{
17 public:
18 virtual void display2(){}
19 };
20
21 int main()
22 {
23 A a;
24 B b;
25 cout << sizeof(a) << endl;
26 cout << sizeof(b) << endl;
27 return 0;
28 }
~
在Gcc上测试,此时sizeof(a)和sizeof(b)输出都是4。 但是在VS2013上测试,sizeof(a) = 4, sizeof(b) = 12.a的大小为4没问题,即一个虚表指针的大小,在两个编译器上大小相同。但是sizeof(b)的大小在两种编译器上却差距很大。奇怪!再试下在A中定义一个int型数据:
#include<iostream>
using namespace std;
class A{
public:
virtual void display1(){}
private:
int x;
};
class B : public virtual A{
public:
virtual void display2(){}
};
class C : public virtual A{
public:
virtual void display3(){}
};
class D : public B, public C{
public:
virtual void display4(){}
};
int main()
{
A a;
B b;
C c;
D d;
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;
cout << sizeof(d) << endl;
system("pause");
return 0;
}
在Gcc上测试,此时的输出是:sizeof(a) = 8, sizeof(b) = 12.但是在VS中测试是:sizeof(a) = 8, sizeof(b) = 16,sizeof(c) = 16, sizeof(d = 24)。A中int型数据+虚表指针大小为8,这个也没有问题。但是b的大小还是不同。编译之后,生成如下窗口信息:
> class A size(8):
1> +---
1> 0 | {vfptr}
1> 4 | x
1> +---
1>
1> A::$vftable@:
1> | &A_meta
1> | 0
1> 0 | &A::display1
1>
1> A::display1 this adjustor: 0
1>
1>
1> class B size(16):
1> +---
1> 0 | {vfptr}
1> 4 | {vbptr}
1> +---
1> +--- (virtual base A)
1> 8 | {vfptr}
1> 12 | x
1> +---
1>
1> B::$vftable@B@:
1> | &B_meta
1> | 0
1> 0 | &B::display2
1>
1> B::$vbtable@:
1> 0 | -4
1> 1 | 4 (Bd(B+4)A)
1>
1> B::$vftable@A@:
1> | -8
1> 0 | &A::display1
1>
1> B::display2 this adjustor: 0
1>
1> vbi: class offset o.vbptr o.vbte fVtorDisp
1> A 8 4 4 0
1>
1>
1> class C size(16):
1> +---
1> 0 | {vfptr}
1> 4 | {vbptr}
1> +---
1> +--- (virtual base A)
1> 8 | {vfptr}
1> 12 | x
1> +---
1>
1> C::$vftable@C@:
1> | &C_meta
1> | 0
1> 0 | &C::display3
1>
1> C::$vbtable@:
1> 0 | -4
1> 1 | 4 (Cd(C+4)A)
1>
1> C::$vftable@A@:
1> | -8
1> 0 | &A::display1
1>
1> C::display3 this adjustor: 0
1>
1> vbi: class offset o.vbptr o.vbte fVtorDisp
1> A 8 4 4 0
1>
1>
1> class D size(24):
1> +---
1> | +--- (base class B)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> | +---
1> | +--- (base class C)
1> 8 | | {vfptr}
1> 12 | | {vbptr}
1> | +---
1> +---
1> +--- (virtual base A)
1> 16 | {vfptr}
1> 20 | x
1> +---
1>
让我们来一个个分析:(1)A的起始地址先存放的是vfptr,即虚函数表指针,占4个字节,然后接下来4个字节存放的是数据x,这样a的大小为8;(2)B虚拟继承自A,首先存放的是自己的vfptr,占4字节。然后是指向虚基类的指针vbptr,也是4字节。最后存放类A,占8字节。这样b的大小为16字节;(3)C与B同理,所以c的大小也是16;(4)D共有继承B和C。首先存放B的虚函数表指针和虚基类指针,占8字节。然后是C的虚函数表指针和虚基类指针,占8字节。最后存放A的数据,占8字节。这样一共24字节。
在VS的实现机制中,虚基类在子类的子类中只有一个实体,子类共享这个实体。子类怎样定位到基类呢?答案就是虚基类指针,指向虚基类的起始地址(我感觉也有可能是偏移值?)。这样在子类的子类及其派生类中永远保证只有一个虚基类的实体。Gcc的实现机制有时间再研究下。