虚继承
虚继承 是 C++ 中的一种特殊继承方式,旨在解决多重继承中的菱形继承问题。
菱形继承问题
菱形继承 是多重继承中的一种特殊情况,通常在有两个或多个派生类继承同一个基类时发生:
cpp
class A {
public:
int a = 1;
};
class B : public A {}; // B 继承 A
class C : public A {}; // C 继承 A
class D : public B, public C {}; // D 继承 B 和 C
int main() {
D d;
d.a; // 错误:产生歧义,是 B::a 还是 C::a
d.B::a; // 正确:明确指定
d.C::a; // 正确:明确指定
}继承结构:
txt
A
/ \
B C
\ /
D问题:D 有两份 A 的副本!
D -> B -> AD -> C -> A
解决方案:虚继承
cpp
class A {
public:
int a = 1;
};
class B : virtual public A {}; // 虚继承
class C : virtual public A {}; // 虚继承
class D : public B, public C {}; // 只有一份 A
int main() {
D d;
d.a = 10; // 正确:只有一份 A,可直接访问
}虚继承的工作原理
虚继承核心思想:无论一个基类在继承链中被虚继承多少次,它在派生类对象中最终只存在一个共享的实例。
为实现该共享,编译器不能再像普通继承那样将基类数据依此堆叠在派生类中,而是引入一个间接层:虚基类表指针(vbptr),指向虚基表(vbtable)。
- 虚基表指针:当一个类(如
B和C)通过virtual public继承一个类(如A)时,编译器会在该类的对象中增加一个隐藏的指针对象,即vbptr。 - 虚基表:记录从当前
vbptr位置 到 共享基类(A)在最终对象(D)中起始位置 的偏移量。
编译器会根据 B 和 C 的 vbtable 中的偏移量,在 D 对象的内存末尾放置唯一的一份基类 A 的数据。

虚继承构造函数顺序
cpp
class A {
public:
A() { cout << "A "; }
};
class B : virtual public A {
public:
B() { cout << "B "; }
};
class C : virtual public A {
public:
C() { cout << "C "; }
};
class D : public B, public C {
public:
D() { cout << "D "; }
};
int main() {
D d; // 输出: A B C D
}顺序:
- 虚基类
A(只构造一次) - 直接基类
B、C(按声明顺序) - 派生类
D
使用建议
虚继承应该用于:
- 真正的"is-a"菱形继承关系
- 接口类(纯虚函数)
避免用于:
- 简单的"has-a"或"uses-a"关系
- 扁平的类层次