C++类模型漫谈(五)
系统基于32位,MSVC编译器,VS开发工具
1、上篇讲到菱形继承,我们发现菱形继承会导致有两份的基类TypeA子对象,因为TypeB继承了TypeA,TypeC也继承了TypeA ,所以TypeB子对象和TypeC子对象都会包含TypeA部分。
可以通过虚继承保持只有一个TypeA子对象,至于如何只出现一份,看下文。
先来个简单的虚继承TypeB虚继承TypeA。
虚继承相对于普通继承,内存中将TypeA子对象放在了b_obj内存的后面部分,而之前普通继承则直接是放在了前面。
现在导致TypeA子对象部分的__vfptr和TypeB部分的__vfptr无法合并。
我们可以看到TypeA的__vfptr的虚表只包含了TypeA定义的2个函数,当然有一个被TypeB重写了,所以虚表中存的是&TypeB::TypeA_Method
TypeB的__vfptr的虚表只包含了TypeB自己定义的那1个函数TypeB::TypeB_Method1,虚表存的是&TypeB::TypeB_Method1
通过这个无法合并的两个虚表,我们看到每个类型的虚表只包含了自己定义的虚函数指针,哪怕重写过或者继承,也不会包含在内
另外我们看到TypeB部分还包含了另一个编译器生成的变量__vbptr (virtual Base Pointer 虚基类表指针),指向一个虚基类偏移表,偏移表存的是相对于每一个虚基类的偏移位置。另外一方面说明,如果只是普通继承,不会往表中存放偏移位置
当然如果一个类没有任何的虚基类,则编译器也不会为其生成__vbptr。
TypeB虚继承TypeA,而TypeA子对象部分地址比TypeB对象起始地址多了16个字节,所以偏移表中存的16,占4个字节
如果TypeB再虚继承TypeC, 而TypeC子对象部分地址比TypeB对象起始地址多24字节,那边偏移表中则会再添加一条数据24(在数据16的后4个字节中存放)
|
![]() |
![]() |
1、通过a_ptr调用TypeA_Method1() 过程也是差不多,要获得a_ptr也是要先通过b_ptr的虚基类偏移表,转换获得a_ptr指针,接下来一样通过a_ptr获取__vfptr并定位到&Type::TypeA_Method1调用,这个时候需要的是TypeB类型对象,则传入b_ptr作为this指针。 2、通过b_ptr调用TypeA_Method2() 跟图例的函数调用过程一样,只不过最后传入函数的this指针为b_ptr+16,即TypeA子对象的地址,因为该函数没有被重写过还是TypeA::TypeA_Method2。 |