C++不同于C#,可以多继承。多继承与python一样。
C++允许一个类继承多个类。
语法:
class 子类:继承方式 父类1, 继承方式 父类2...
多继承可能会引发多个父类中同时出现同名成员,需要加作用域加以区分。
菱形继承(钻石继承)
这种继承有两个问题:
-
羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
-
草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
class Animal
{
public:
int m_Age;
};
class Sheep :public Animal {};
class Camel :public Animal {};
class SheepTuo:public Sheep, public Camel{};
出现问题:二义性。
对于羊驼类对象来说,他继承了两个父类的共有属性。此时给这个属性赋值,无法判断到底给哪个属性赋值。因此会报错。
解决方案:
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 10;
st.Camel::m_Age = 28;
cout << st.Sheep::m_Age << endl;
cout << st.Camel::m_Age << endl;
// 当菱形继承,两个父类拥有相同的数据,需要加以作用域区分。
// 对于这个年龄(性别等)这个属性,只要有一份就够了,菱形继承导致了数据有两份。资源浪费,且容易歧义。
}
结论:
- 当菱形继承,两个父类拥有相同的数据,需要加以作用域区分。
- 对于这个年龄(性别等)这个属性,只要有一份就够了,菱形继承导致了数据有两份。资源浪费,且容易歧义。
通过开发人员命令提示符工具:查看如下:
如何彻底解决这些问题呢?
哲学:靠事物本身不能解决的,就要考虑引入新的事物:虚继承。
利用虚继承来解决菱形继承的问题。
虚继承:在继承方式之前加上关键字:virtual,将继承变成虚继承。
此时Animal类被称为 虚基类。
当虚继承之后,这个m_Age就只剩下一个了。此时再用st.m_Age直接调用就不会报错了,因为明确唯一性了。
我们查看一下对象模型:只有一个m_Age属性了。
底层如何实现的?
查看对象模型发现:多了两个vbptr:虚基类指针。v-virtual; b-base; ptr-pointer。
这个指针会指向vbtable:虚基类表。
这个表中:会记录一个数据:数据是一个偏移量offset。
通过这个偏移量,指针也能找到属性所在的位置:当前属性在8。
- 在Sheep中的表记录为8。Sheep的位置是0,0偏移8,正是属性m_Age的位置。
- 在Camel中的表记录为4,Camel的位置为4,4偏移4,正式属性m_Age的位置。
两个指针指向同一份数据,所以不论从哪里继承,都是只有一份。通过虚继承解决了二义性。
总结:
-
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
-
利用虚继承可以解决菱形继承问题