一、虚继承的诞生原因
普通多重菱形继承存在两个严重问题:
公共基类会被拷贝多份存放在最终子类对象中,造成数据冗余。
访问公共基类成员时存在二义性,编译器不知道使用哪一份父类副本。
虚继承的唯一作用: 让多个父类,共享同一份公共基类子对象,保证最终子类中公共基类唯一。
二、虚继承关系模型(菱形结构)
A(公共基类)
/ \
virtual virtual
B C
\ /
D
B、C 均采用虚继承方式继承 A。
三、虚继承最核心、独有的机制(与普通继承最大区别)
1. 普通继承机制
构造是逐层向上传递的: D → 构造 B、C → B、C 各自构造 A 最终得到 两份 A
2. 虚继承机制(特殊规则)
虚基类不会由中间父类构造,只会由「当前最底层子类」唯一构造一次。
重点精确定义:
当创建 B、C 对象(自己是最底层) B、C 正常独立构造属于自己的 A 对象。
当创建 D 对象(D 是最底层)
B、C 正常完整构造自身,不会被跳过、不会被忽略
唯独 B、C 内部继承链中构造 A 的行为被完全禁止
唯一由 D 负责构造公共 A 对象,且只构造一次
关键区分: 被限制的只有「中间类构造虚基类」这一条。 中间类自身的构造、自身成员初始化、自身逻辑完全正常执行。
四、为什么要这样设计机制
为了唯一性: 如果允许 B、C 各自构造 A,D 内部又会出现两份 A,虚继承就彻底失效。
所以编译器强制一刀切: 在菱形继承的最终子类层面,剥夺所有中间父类构造虚基类的权利,统一交给最底层子类。
五、虚继承的内存布局机制
普通菱形继承内存布局
D
B 的 A 副本
B 自身成员
C 的 A 副本
C 自身成员
虚继承内存布局
D
B 自身成员 + vbptr(虚基类指针)
C 自身成员 + vbptr(虚基类指针)
唯一共享的 A 子对象
vbptr 作用
每个虚继承的子类,都会自带一个虚基类指针 vbptr。 vbptr 指向一张虚基类表,记录共享基类相对于当前对象的内存偏移。
通过偏移地址,B、C 可以间接、共享式访问唯一的 A 对象。
六、虚继承构造执行顺序(固定机制)
创建最终子类 D 对象时,严格顺序:
优先构造所有虚基类(仅一次,由 D 完成)
再依次构造普通直接父类 B、C
最后构造自身 D
这是 C++ 标准固定顺序,和普通继承顺序完全不同。
七、虚继承总结
虚继承解决菱形继承多副本问题,实现公共基类全局唯一。
虚继承彻底改变了继承构造传递机制:中间类不再构造虚基类。
虚基类永远由当前实例化的最底层子类唯一构造。
中间父类只是失去了 “构造虚基类” 的权限,自身构造完全正常。
通过 vbptr + 偏移表,实现多个父类远程共享同一份基类数据。