虚继承
参考资源:C++之虚函数与虚继承详解
1、背景
从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:
1)浪费存储空间;
2)存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。
2、概念
虚继承是解决C++多重继承问题的一种手段。将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
class A
/ \
`虚继承` / \ `虚继承`
/ \
class B class C
\ /
继承 \ / 继承
\ /
class D
3、原理
虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
4、虚继承内存占用
示例程序:
#include <iostream>
using namespace std;
//动物类
class Animal{
public:
int m_Age;
int m_Age1;
int m_Age2;
int m_Age3;
};
#if 0 //写法1:虚继承
//继承之前 加上关键字 virtual变为虚继承
//羊类
class Sheep:virtual public Animal{};
//驼类
class Tuo :virtual public Animal{};
//羊驼类
#else // 写法2
//羊类
class Sheep:public Animal{};
//驼类
class Tuo :public Animal{};
//羊驼类
#endif
class SheepTuo :public Sheep,public Tuo{};
void test02(){
cout << sizeof(Sheep) << " " << sizeof(Tuo) << " " << sizeof(SheepTuo) << endl;
}
int main() {
test02();
system("pause");
return 0;
}
讨论与分析
1)方法1使用虚继承方式,输出为20 20 24。
分析:
class B包含class A基类的4个成员和1个虚继承指针,所以一共是20字节;
class D包含4个基类成员和1个class B的虚继承指针和1个class C的虚继承指针,所以一共是24字节;
2)方法2不使用虚继承方式,输出为16 16 32
class B包含class A基类的4个成员,所以一共是16字节;
class D继承class B、C,而class B、C从不同途径继承来的同一基类,会在子类中存在多份拷贝,因此class D分别有2份class A基类的4个成员,所以一共是32字节;
本文详细探讨了C++中的虚继承,主要解决多重继承带来的存储空间浪费和二义性问题。通过虚继承,可以确保内存中只有一份共同基类的拷贝,减少存储开销,并消除基类成员访问的不确定性。同时,介绍了虚继承的底层实现原理,包括虚基类指针和虚基类表,并通过实例分析了虚继承与非虚继承在内存占用上的区别。
2311

被折叠的 条评论
为什么被折叠?



