什么是虚拟继承

由于C++支持多继承,除了public、protected和private三种继承方式外,还支持虚拟(virtual)继承,举个例子:

#include <iostream>
using namespace std;

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

int main()
{
    cout << "sizeof(A):" << sizeof A << endl; // 1,空对象,只有一个占位
    cout << "sizeof(B):" << sizeof B << endl; // 4,一个bptr指针,省去占位,不需要对齐
    cout << "sizeof(C):" << sizeof C << endl; // 4,一个bptr指针,省去占位,不需要对齐
    cout << "sizeof(D):" << sizeof D << endl; // 8,两个bptr,省去占位,不需要对齐
}

/*
    sizeof(A):1
    sizeof(B):8
    sizeof(C):8
    sizeof(D):16
*/

上述代码所体现的关系是,B和C虚拟继承A,D又公有继承B和C,这种方式是一种菱形继承或者钻石继承,可以用如下图来表示:

 

 

虚拟继承的情况下,无论基类被继承多少次,只会存在一个实体。**虚拟继承基类的子类中,子类会增加某种形式的指针,或者指向虚基类子对象,或者指向一个相关的表格;表格中存放的不是虚基类子对象的地址,就是其偏移量,此类指针被称为bptr,如上图所示。如果既存在vptr又存在bptr,某些编译器会将其优化,合并为一个指针。

虚拟继承是一种多重继承的特殊形式,它的目的是解决菱形继承中的冗余数据和二义性问题¹。

菱形继承是指一个派生类继承了两个或多个直接基类,而这些直接基类又继承自同一个间接基类,形成了一个菱形的继承结构²。例如:

class A {
  // 间接基类
};

class B: public A {
  // 直接基类
};

class C: public A {
  // 直接基类
};

class D: public B, public C {
  // 派生类
};

在这种情况下,派生类 D 中会包含两份间接基类 A 的成员,一份来自 B,一份来自 C。这样会造成以下问题³:

  • 冗余数据:派生类 D 中的两份 A 的成员占用了多余的内存空间,而且可能存储了不一致的数据。
  • 二义性:当派生类 D 中访问 A 的成员时,编译器无法确定是访问 B 中的 A 还是 C 中的 A,需要显式地指定路径,否则会报错。

为了解决这些问题,可以使用虚拟继承,即在继承方式前加上 virtual 关键字,表示该基类是一个虚拟基类,例如:

class A {
  // 间接基类
};

class B: virtual public A {
  // 直接基类
};

class C: virtual public A {
  // 直接基类
};

class D: public B, public C {
  // 派生类
};

这样,派生类 D 中就只保留了一份间接基类 A 的成员,不会出现冗余数据和二义性问题。虚拟继承的原理是通过虚基指针和虚基表来实现的,每个包含虚拟基类的类都有一个虚基指针,指向一个虚基表,虚基表中存放了虚拟基类的偏移量,用于定位虚拟基类的位置⁴。

虚拟继承的作用是在多重继承的情况下,保证间接基类的实例只会在派生类对象中存在一次,而不会重复出现。这样可以避免数据的冗余和访问的二义性,也可以节省内存空间和提高效率。虚拟继承是一种复杂的继承方式,一般不推荐使用,除非在必要的情况下。多重继承本身就容易引起设计上的混乱和错误,所以在实际开发中应该尽量避免使用多重继承,或者使用单一继承和组合的方式来代替多重继承。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值