-
一般继承(无虚函数覆盖)
- 只有一个虚指针,指向一个虚表,虚函数按顺序从祖先节点开始插入到虚表上。
- 字段按顺序从祖先节点开始插入到对象内存上
-
一般继承(有虚函数覆盖)
- 只有一个虚指针,指向一个虚表,虚函数按顺序从祖先节点开始,先查找是否有可以覆盖的,如果有,覆盖掉,否则插到最后。
- 字段按顺序从祖先节点开始插入到对象内存上
-
多重继承(有虚函数)
- 根据继承的类的数量来一一创建各自内存块,如继承了A,B,C三个类,则先后创建ABC三块,每一个均有自己的虚指针及字段,子类的虚函数分别遍历三个虚指针指向的虚表查找是否产生覆盖,如果有,则覆盖,如果没有则插入到在第一个父类的虚表里
- 每个类的字段按顺序跟在各个类内存块的虚指针后面。
-
重复继承(钻石模型)
-
此类型与多重继承类似,先把最底层的子类所继承的若干个间接父类(如B1, B2)当作一个整体,把它们先按单一继承创建,再根据多重继承的性质创建。
-
-
钻石型多重虚拟继承
-
class B {……};class B1 : virtual public B{……};class B2 : virtual public B{……};class D : public B1, public B2{ …… };
- 虚拟继承的就是为了解决重复继承中多个间接父类的问题。大体思想是:先从间接父类开始创建,把顶层的超类放在内存模型的最后,同时间接父类的虚表只能含有本身的虚函数(同时不能包含顶层超类的虚函数)和子类的虚函数。
- 间接父类每一个虚指针后紧一个vbptr虚基类表指针,变量指向一个全类共享的偏移量表,表中项目记录了对于该类而言,“虚基类表指针”与虚基类之间的偏移量。
- 内存模型创建过程:
- 先创建间接父类B1, 虚继承先创建虚指针,紧跟vbptr,接着是成员变量;遍历B1的虚函数,检查B类中是否有,如果有,则跳过;如果没有,则插入到函数表里。此时B1内存块为:vptr, vbptr, ib1, cb1; 虚函数表有:B1::f1, B1::Bf1;
- 重复上面步骤继续创建B2,此时内存块为:vptr, vbptr, ib1, cb1, vptr, vbptr, ib2, cb2; B2虚函数表有:B2::f2, B2::Bf2;
- 创建完间接父类,继而创建子类。把子类的字段按顺序紧放在内存后面,子类的函数在间接父类中接按顺序查找是否产生覆盖(同时不能包含顶层超类的虚函数),如果有,则覆盖,继续下一个函数。例如先遍历D::f,由于顶层超类包含了,跳过不处理;对于D::f1,在B1的虚函数表里找到了,则覆盖,此时B1的虚函数表为:D::f1, B1::Bf1。对于D::f2,覆盖了B2的B::f2,B2虚函数表有:D::f2, B2::Bf2; 对于D::Df,没有找到覆盖的,则插到第一个间接父类的虚函数表里。此时B1的虚函数表为:D::f1, B1::Bf1,D::Df。
- 最后创建顶层超类。按常规创建B类,创建完之后,用D类的虚函数遍历检查是否产生覆盖,如果有,则覆盖,否则跳过。
参考内容:
钻石型继承模型的内存分布
C++ 对象的内存布局 -