Skip to content

虚继承 ​

虚继承 是 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 -> A
  • D -> 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
}

顺序:

  1. 虚基类 A(只构造一次)
  2. 直接基类 B、C(按声明顺序)
  3. 派生类 D

使用建议 ​

虚继承应该用于:

  • 真正的"is-a"菱形继承关系
  • 接口类(纯虚函数)

避免用于:

  • 简单的"has-a"或"uses-a"关系
  • 扁平的类层次