持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情
1、什么是继承
封装、继承和多态面向对象的三大特征,继承最直观的好处就是可以少些代码,许多相似重复的代码可以只写一次,提高代码的复用率。在我的C++专栏中有一篇文章详解了三种继承的方式。 [](神奇的继承 - 掘金 (juejin.cn))
2、多继承
C++中允许一个子类继承多个父类,这是C++语言比较特殊的地方,其他们多面向对象编程语言如java是不允许多继承的。多继承在有些时候可能会比较方便,但他也可能会引发一个新的问题,即菱形继承。
我们创建一个father类两个子类son1,son2都继承father,再创建一个greadson即继承son1有继承son2。 这样是四个类就会构成一个菱形继承的问题。
#include <iostream>
using namespace std;
class father
{
public:
int age;
};
class son1 :public father
{
};
class son2 :public father
{
};
class greadson :public son1, public son2
{
};
void test01()
{
greadson p1;
p1.son1::age = 10;
p1.son2::age = 20;
cout << "p1的年龄:" << p1.son1::age << endl;//因为是多继承,所以在使用两个父类的同名属性时必须加作用域,否则编译器会报错。
cout << "p1的年龄:" << p1.son2::age << endl;
}
int main()
{
test01();
}
从上图的运行结果来看,程序使用可以运行的,但是在实际问题中,一个人不能有两个年龄。这就是菱形继承所隐含的问题。
3、解决方案
1、方法一
当然是尽量不用菱形继承啦。
2、方法二
使用虚继承,只需要在继承的语法上加上virual即可。
class son1 :virual public father
class son2 :virual public father
这个时候可以发现,两次输出年龄都是20,第一次的赋值会被地如此赋值修改。
4、底层解析
打开VS2017的开发人员命令符
1、cd 代码的文件夹所在位置
2、dir 查看里面的内容
3、cl /d1 reportSingleLayoutgreadson "源.cpp"
得到下面截图:
图中的vbptr 是virual base pointer 虚基类指针的意思。他会指向vbtable(virual base table 虚基类表格)就是图中的红色箭头,这个表格会记录一个偏移量,即黄绿箭头所指的数字。通过偏移量我们可以找到唯一age的位置。
如果不采用虚继承会出现如下情况
对比会发现图中会有两份age,而上图只有一份age。