作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee✨
作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
相信大家再学习了前面的基础继承知识后,对继承已经不在陌生了,今天博主就来上一个有难度的东西,需要大家对前面说到切片知识以及对解引用知识的掌握,才能更好的学习这篇讲到的内容,话不多说,我们开始进入正文
我们的C++语言是一个支持多继承的语言,特也可以支持单继承语言。
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
就是由于多继承的存在,就必然会存在下面的结构:这就是菱形继承
再上篇我们说到的继承后基类子类再内存当中是怎么样存储的,那么菱形继承的再内存的存储就会是下面这种情况:
可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。 数据冗余是因为有两份相同的数据再Assistant对象当中,二义性是不知道访问哪个_name.有的人会说,我作为老师是一个名字,作为学生又是一个名字不就行了??但是Person里面不止一个属性,可能还有身份证号,年龄这些的。这里面的重复的属性应该有唯一性,如果是菱形继承会发生下面这种情况:
我们发现我们的基类A里面的属性_a和_z最后的结果不统一,修改的不是同一个属性,万一我修改的属性是身份证号,这样就导致我有两个身份证号,肯定是不合理的,而且那么使用的时候就会有存在二义性,必须指定类域,这样的设计显然不符合我们的标准。
所以想要解决这个问题,我们要知道解决后我们想要的是什么效果,我们知道继承之后,子类就可以访问父类的成员,子类对象修改父类的成员变量时,父类本身的对象里面的属性也应该有被修改,需要同时变.应该是下面的效果:
我们发现这才是我们想要的效果,一个就四个属性,我们通过BC父类来修改基类的属性,也会发生修改,最后的结果为最后一次修改的结果,这样就不会发生二义性。
为什么最上面会出现两个不同的结果??
原因是上面的菱形继承图,每个属性再内存当中都对应一个地址,而B,C对象里面都有_name属性,所有就有两个地址,指定修改哪个就修改哪个地址
通过上面两个画图表示,我们已经知道菱形继承为什么会有我们所担心的问题了,那我们怎么解决这个问题,我们通过一个关键字virtual,来修饰腰部的类即如下面所示:
这样就解决我们的问题,我们来看看内存当中是怎么存放的:
大家清楚看看我们基类的两个属性再内存中只有一份地址,并且值为最后一次修改的值,通过virtual关键字就可以使我们的两份重复数据变成了一份,以解决了二义性的问题。
虚基表指针:
我们来看它到底使用什么办法去解决这个问题的???,我们这一部分是怎么也能访问到我们基类里面的变量的??
通过在内存里面的图来看,我们发现多了两个东西,这存的也不是值啊,应该是地址,我们来看看这地址指向什么地方:
通过上图我们可以发现这个地址里面的第一行是为其他位置预留空间,第二行存放的是偏移量,方便找到基类里面的属性,只要找到第一个,其他基类的属性是连在一起的,就在下面。所以只会有一个偏移量,编译器不会把基类的属性放在不连续的地址上。因为都是归属最后一个类对象的。不是单独的属于腰部类里面的对象了,所以需要通过偏移量去找
重要:(virtual后) 那D部分中的B C为甚要去找属于自己那一部分的A,原因是我们之前说过的对象的赋值,B和C是D的父类,子类对象可以赋值给父类对象,就会出现这种情况:
D d; B b=d; C c=d;
通过偏移量找到属于自己那一部分的A在赋值给 b或c对象
在看看我们定义腰部类对象的时候内存是什么样的,是按照之前一样把基类的属性放在一起,还是也通过偏移量找基类的位置??
这样设计的好处,因为编译器就按照相同的方式给你查找,编译器又不知道你是什么类型,加了virtual,都是按照虚地址映射表去找
多个对象的是也是可以通过虚基表去找对应的偏移量,虚基表里面可能还有其他值要放,所有不能把指向虚基表的地址直接替换成偏移量的值
通过上面的理解相信大家应该知道我们的祖师爷是怎么解决这个问题了吧,我们通过看大小也能发现解决了数据冗余的问题,**对于基类中只有单个属性的时候或者属性大小比较的时候,节省空间效果不是很明显,**但是当基类的属性大小较大的时候就能体现出来
注意:我们对象的空间排序是先继承在上面,刚才的d对象,因为先继承了B在继承了C,所以B在上,C在下
// Car和BMW Car和Benz构成is-a的关系 下面是继承
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "陕ABIT00"; // 车牌号
};
class BMW : public Car{
public:
void Drive() {cout << "好开-操控" << endl;}
};
class Benz : public Car{
public:
void Drive() {cout << "好坐-舒适" << endl;}
};
// Tire和Car构成has-a的关系 下面是组合
class Tire{
protected:
string _brand = "Michelin"; // 品牌
size_t _size = 17; // 尺寸
};
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "陕ABIT00"; // 车牌号
Tire _t; // 轮胎
};