虚继承必须所有中间路径均声明virtual,否则仍产生重复基类实例;最派生类须显式调用虚基类构造函数;虚继承引入vbptr开销且访问更慢;虚基类析构函数必须为virtual以避免未定义行为。

虚继承必须在所有中间路径上都声明
只在某一条继承路径上加 virtual 没用。比如 Derived1 虚继承 Base,但 Derived2 是普通继承,那么 Final 仍会得到两份 Base 实例。
正确做法是:所有直接继承虚基类的中间类,都必须显式使用 virtual:
class Derived1 : virtual public Base {};class Derived2 : virtual public Base {};class Final : public Derived1, public Derived2 {};
漏掉任何一个 virtual,编译器就按普通多重继承处理,二义性和重复构造照旧发生。
最派生类必须显式调用虚基类构造函数
虚基类的构造函数不会由中间类自动触发,而是由最终派生类(如 Final)在初始化列表中直接调用。否则编译报错:call to implicitly-deleted default constructor of 'Base'。
常见错误写法:
class Final : public Derived1, public Derived2 {
public:
Final() { /* 错误:没在初始化列表里调用 Base 构造函数 */ }
};
正确写法:
class Final : public Derived1, public Derived2 {
public:
Final() : Base(), Derived1(), Derived2() {} // Base() 必须显式出现
};
注意顺序:即使 Base() 写在最后,它也一定最先执行;Derived1 和 Derived2 的构造函数内部不会再调用 Base 的构造函数。
虚继承带来间接访问开销,别滥用
虚继承不是银弹。每个虚继承路径都会引入一个 vbptr 指针(通常 8 字节),并依赖 vbtable 查找虚基类成员偏移。这意味着:
- 对象尺寸变大(尤其多层虚继承时)
- 访问虚基类成员需一次指针解引用,比普通继承慢一点
- 调试时观察内存布局更复杂,
offsetof对虚基类成员可能返回负值或不可靠
仅当真正存在菱形继承且需要共享状态时才用虚继承。接口类(无数据成员)通常不需要——纯虚函数本身不引发二义性,也不需要共享实例。
delete 虚基类指针前必须确保析构函数为 virtual
这是最容易被忽略的运行时陷阱。如果通过 Base* 指向一个虚继承链中的对象(如 new Final),而 Base::~Base() 不是 virtual,delete 会导致未定义行为——不仅析构不完整,还可能因 vbptr 偏移问题崩溃。
必须保证:
-
Base的析构函数声明为virtual ~Base() = default; - 不能用
Base*指向栈对象后调用delete(哪怕析构函数是 virtual)
虚继承本身不改变析构顺序规则,但放大了析构不完整的后果:虚基类子对象若未被正确析构,其资源泄漏或双重释放风险更高。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











