一、什么是菱形继承?
C++具备其他语言所没有的多继承特性,一个子类可以继承自多个父类,这些父类可能继承自相同的父类,从而就造成了菱形继承。
菱形继承的问题是,一个是浪费存储空间,一个是二义性。这些可以通过代码展示:
#include <iostream>
using namespace std;
class A
{
public:
void func1();
void func2();
int num;
};
class B1 : public A
{
public:
void func1();
};
class B2 : public A
{
public:
void func2();
};
class C : public B1, public B2
{
};
int main()
{
C c;
c.B1::num = 1;
c.B2::num = 2;
cout << "c.B1::num " << c.B1::num << " c.B2::num " << c.B2::num << endl;
return 0;
}
运行结果:
c.B1::num 1 c.B2::num 2
期望的运行结果应该是两个num的打印值都是2,即在两次对num的赋值中,第2次赋值覆盖第一次赋的值,从运行结果来看,显然并不是这样的。
使用g++ -fdump-lang-class virtual-inheritance.cpp -o vi 这个命令来查看类的内存模型,其中, -fdump-lang-class是gcc 8.0以上使用的命令,gcc 8.0以下使用的参数为-fdump-class-hierarchy,可以看出在Class C中,两个Class A的地址并不一样!Class C在构造的时候构造了两次Class A
解决这个问题的方法就是使用虚继承,即将代码中B1类,B2类的继承方式改为虚继承:
即将上述代码中的
class B1 : public A
class B2 : public A
改为
class B1 : virtual public A
class B2 : virtual public A
此时再来查看类的模型
此时可以清楚地看到在Class C中,两份A的地址是一样的!子类(C)只继承了一次父类的父类(A)。虚继承是通过虚表指针偏移来实现的。父类(B1 ,B2)的 vptr 都有到共同基类(A)的偏移量,从而让子类(C)在多继承时指向同一个父类的父类(A)
此时执行可执行文件 vi 进行验证,
执行结果:
c.B1::num 2 c.B2::num 2
符合预期!