单一继承体系下:
在vs2005下查看每一个层次的对象内存布局:
Paraent对象内存布局
Paraent占8个字节,其中m_iParaent占4个字节,虚表指针占4个字节,也就是说虽说成员函数出现在了类的声明中,但实际上是不出现在object中的。首先出现的是虚表指针,然后是按照声明顺序出现的数据成员。
而对于下面的single类的实例,至少占据1个字节,
class single
{
};
Child对象内存布局
Child占据12个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节。
而且只有一个虚表,虚表中Child重载的基类f方法占据了Paraent::f的位置,而其余的位置还是按照Paraent续表的顺序,之后是Child独自拥有的方法的指针。
GrandChild的内存布局
GrandChild占16个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节,m_iGrandChild占4个字节。
仍然只有一个虚表,虚表中f的位置替换成了Grang::f,而后是Paraent的方法,而后是Child的方法,而chuild中有被GrangChild重载的方法g_chinld,这个位置被GrangChild::g_child替换掉。
在VS下的测试输出如下:
可见以下几个方面:
虚函数表在最前面的位置。
成员变量根据其继承和声明顺序依次放在后面。
在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。
多重继承
占据28个字节,其中4个数据成员共16字节,3个虚表指针共12字节。其中Derive和Base1共享的虚表是主要的虚表,f独有的方法指针都是添加到和Base1共享的续表中;f重载的方法,如果在Base1,2中有该方法,则被overwrite的虚函数在虚函数表中也会得到更新。
可以看到:
- 每个父类都有自己的虚表。
- 子类的成员函数被放到了第一个父类的表中。
- 内存布局中,其父类布局依次按声明顺序排列。
- 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
重复继承
D占48字节,10个整型变量占40个字节,两个虚表指针占8个字节。
可以看见,顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:
D d;
d.ib = 0; //二义性错误
d.B1::ib = 1; //正确
d.B2::ib = 2; //正确
虽然这种方法消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复, C++引入了虚基类的概念避免这个问题。
钻石型多重虚拟继承
这时候的B1的对象的内存布局如下:
B1占据32字节,4个整型变量占16字节,3个虚表占12字节,补齐4个字节.
D的内存占据情况:
1>class D size(56):
1> +---
1> | +--- (base class B1)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> 8 | | ib1
1>12 | | cb1
1> | | <alignment member> (size=3)
1> | +---
1> | +--- (base class B2)
1>16 | | {vfptr}
1>20 | | {vbptr}
1>24 | | ib2
1>28 | | cb2
1> | | <alignment member> (size=3)
1> | +---
1>32 | id
1>36 | cd
1> | <alignment member> (size=3)
1> +---
1>40 | (vtordisp for vbase B)
1> +--- (virtual base B)
1>44 | {vfptr}
1>48 | ib
1>52 | cb
1> | <alignment member> (size=3)
1> +---
1>D::$vftable@B1@:
1> | &D_meta
1> | 0
1> 0 | &D::f1
1> 1 | &B1::Bf1
1> 2 | &D::Df
1>D::$vftable@B2@:
1> | -16
1> 0 | &D::f2
1> 1 | &B2::Bf2
1>D::$vbtable@B1@:
1> 0 | -4
1> 1 | 40 (Dd(B1+4)B)
1>D::$vbtable@B2@:
1> 0 | -4
1> 1 | 24 (Dd(B2+4)B)
1>D::$vftable@B@:
1> | -44
1> 0 | &(vtordisp) D::f
1> 1 | &B::Bf
1>D::f this adjustor: 44
1>D::f1 this adjustor: 0
1>D::f2 this adjustor: 16
1>D::Df this adjustor: 0
1>vbi: class offset o.vbptr o.vbte fVtorDisp
1> B 44 4 4 1