菱形继承和虚拟菱形继承
- 菱形继承
- 1. 概念
- 2. 产生的问题
- 虚拟菱形继承
- 1.1 使用
- 1.2 原理
菱形继承
1. 概念
菱形继承是多继承的一个特殊情况,多继承是指一个子类类继承了两个或以上的直接父类,而菱形继承问题的产生是因为该子类的父类,继承了同一个父类,注意是父类而不是直接父类。下面这张图就是直接父类,因为形状像一个菱形就称为菱形继承,但如果不是直接父类,这个形状就不是菱形了,但还是菱形继承。
2. 产生的问题
那菱形继承会导致什么问题呢?由图可以很容易看出,这样会导致D对象有两份A,会产生数据冗余和二义性的问题。
我们通过代码看看A是不是有两份
class A
{
protected:
int _a;
};
class B :public A
{
protected:
int _b;
};
class C : public A
{
protected:
int _c;
};
class D : public B, public C
{
protected:
int _d;
};
int main()
{
D d;
cout << sizeof(d);
return 0;
}
再看看是不是有二义性问题
class A
{
public:
int _a;
};
class B :public A
{
protected:
int _b;
};
class C : public A
{
protected:
int _c;
};
class D : public B, public C
{
protected:
int _d;
};
int main()
{
D d;
cout << d._a;
return 0;
}
下面的虚拟菱形继承可以解决这些问题。
虚拟菱形继承
1.1 使用
使用非常的简单,我们只需要在继承的时候加上virtual关键字就行了,但要注意的是virtual只需要加在会产生两份的继承位置,比如B、C都继承A,这就需要加上virtual关键字,而D继承B、C就不需要用关键字。
class A
{
public:
int _a = 1;
};
class B :virtual public A
{
protected:
int _b = 2;
};
class C :virtual public A
{
protected:
int _c = 3;
};
class D : public B, public C
{
protected:
int _d = 4;
};
int main()
{
D d;
cout << d._a;
return 0;
}
1.2 原理
我们可以通过成员对象模型来研究原理。
我们通过内存窗口先来看一下没有虚拟继承的菱形继承对象模型
很明显的可以看到A有两份,造成了数据冗余和二义性,我们在来看一下虚拟继承以后的对象模型。
可以看到A只有一份,成功的解决了问题,但又产生了一个新问题,A不在B和C里,那B和C如何找到A呢?看上面的模型,细心的同学会发现B和C多了一个东西,那个东西就是解决这个问题的关键,叫做虚基表指针,指向的是一个虚基表,表里面存放的是偏移量,通过这个偏移量找到A。