菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
典型的菱形继承案例:
菱形继承问题:
- 羊继承了动物的数据,驼同样继承了动物的数据,当*使用数据时,就会产生二义性。
- *继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
第一版代码(未使用虚继承)
#include<iostream>
#include<string>
using namespace std;
class animal//父类
{
public:
int m_age;
};
class sheep:public animal //sheep继承了animal 类
{
};
class tuo:public animal //tuo也继承了animal 类
{
};
class sheeptuo :public sheep, public tuo //sheeptuo 又多继承了sheep 和 tuo 类
{
};
void main()
{
sheeptuo s;
//s.m_age;//不明确
cout << "sheeptuo所占空间:" << sizeof(sheeptuo) << endl; //8 因为继承了两个age
s.sheep::m_age = 200;
s.tuo::m_age = 300;
cout << "sheep中age:" << s.sheep::m_age << endl;
cout << "tuo中age:" << s.tuo::m_age << endl;
}
第二版代码(使用虚继承)
#include<iostream>
#include<string>
using namespace std;
class animal//父类
{
public:
int m_age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class sheep:virtual public animal //sheep继承了animal 类,虚继承
{
};
class tuo:virtual public animal //tuo也继承了animal 类,虚继承
{
};
class sheeptuo :public sheep, public tuo //sheeptuo 又多继承了sheep 和 tuo 类
{
};
void main()
{
sheeptuo s;
//12字节 因为继承了一个age,使用虚继承又增加了两个指针,两个指针都指向age这个变量;所有的age都是同一个,都是在对同一个数据操作
cout << "sheeptuo所占空间:" << sizeof(sheeptuo) << endl;
s.sheep::m_age = 200;
s.tuo::m_age = 300; //以最后的赋值数据为准,之前的都被覆盖
cout << "sheep中age:" << s.sheep::m_age << endl;
cout << "tuo中age:" << s.tuo::m_age << endl;
cout << "sheeptuo中age:" << s.m_age << endl;
}
可以看一下内存模型分布
增加了两个vbptr指针, v:virtual b:base ptr:pointer
vbptr指向vbtable虚基类表,第一个vbptr指向 sheep的虚基类表,偏移量为8,偏移量加8之后就找到了age这个变量;第二个vbptr指向 tuo的虚基类表,偏移量为4,偏移量加4之后就找到了age这个变量;他们指向的都是同一个变量。
总结
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
- 利用虚继承可以解决菱形继承问题