c++默认多重继承按路径复制基类,导致菱形继承中出现冗余子对象和二义性;虚继承通过虚基表共享唯一基类实例,统一语义并消除歧义,但引入运行时偏移计算开销。

因为默认继承是“按路径复制”的:每条继承路径都独立构造一份基类子对象,不是共享。
普通多重继承时,编译器为每条路径单独分配基类内存
比如 class B : public A 和 class C : public A,再有 class D : public B, public C,那么:
-
B对象里自带一块完整的A子对象(含自己的age、vptr等) -
C对象里也自带另一块完整的A子对象 -
D继承B和C,自然就包含这两块互不相干的A副本
这不是 bug,是 C++ 继承模型的底层设计——它把基类当作“嵌入式部件”来处理,而不是“逻辑身份”。所以 D 里真有两份 A::age,改一个不影响另一个。
访问 d.age 报错,本质是编译器拒绝做“猜题”
当你写 d.age,编译器看到两条合法路径:B::A::age 和 C::A::age。它不会帮你选,直接报错:
error: request for member 'age' is ambiguous
你必须显式指定:
-
d.B::age或d.C::age -
static_cast<b>(&d)->age</b>或static_cast<c>(&d)->age</c>
但这种写法本身就暴露了模型缺陷:同一语义(“这个对象的年龄”)被分裂成两个物理存储。
虚继承不是“修复”,而是换了一种对象布局策略
加 virtual 后,B 和 C 不再各自携带 A,而是通过虚基表(vbptr)间接引用同一份 A 实例。关键变化有:
-
A的构造由最派生类D直接调用,只执行一次 -
B和C的构造函数里,对A的构造调用被忽略(语法上仍可写,但无效) -
D对象内存中只有一份A成员,vbptr指向虚基表,靠偏移量定位A字段
代价是每次访问 A 成员都要查表+计算偏移,比普通继承略慢;好处是语义统一、无二义性。
真正容易被忽略的是:虚继承改变的不只是数据数量,而是整个构造时序和对象寻址方式。哪怕你没显式访问 age,只要用了多态(比如 A* 指针指向 D),非虚继承下就可能调到错误的 vptr,导致运行时行为异常。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











