8-5:C++继承之多继承,菱形继承,虚继承,虚基表,继承和组合
发布日期:2021-05-06 07:22:46 浏览次数:39 分类:精选文章

本文共 2020 字,大约阅读时间需要 6 分钟。

文章目录

一:菱形继承与菱形虚拟继承

(1)多继承

之前我们所讲得继承全部属于单继承:一个子类只有一个直接父类

在这里插入图片描述
由于在现实生活中,一个人可能会有双重角色。比如研究生可以作为助教,一方面它是学生一方面它是老师,这样一来,一个子类就有两个及以上的直接父类了,称这种继承为多继承
在这里插入图片描述

(2)菱形继承

C++有很多让人觉得不满意的地方,其中有一个地方就是多继承带来的菱形继承问题,抽象来看,继承关系呈现一种环形结构

如下,一个助教可以是老师也可以是学生,老师和学生同时又是人

在这里插入图片描述
这种继承关系会引发数据冗余和数据二义性的问题
在这里插入图片描述

  • 数据冗余:助教里面Person会被继承两次
  • 数据二义:助教的姓名到底应该是哪一个(虽然现实生活中会存在这种起别名的现象,但是我们考虑的是如身份证那样不可二义的对象)

如下代码反应的就是上述继承关系,最终编译器报错:对于name不明确

class Person{   public:	string _name;//姓名};class Student : public Person{   protected:	int _num;//学号};class Teacher : public Person{   protected:	int _id;//工号};class Assistant : public Student, public Teacher{   protected:	string _course;//主讲课程};int main(){   	Assistant a;	a._name = "xiaowang";}

在这里插入图片描述

  • 上面体现的二义的问题,这个问题当然可以通过加入域作用限制符指定父类解决,但是仍然无法解决属于冗余的问题

(3)虚继承

A:如何解决

如果解决上面的问题就要使用虚继承,只需使用关键字virtual,在下面的位置加入即可

在这里插入图片描述

class Student : virtual public Person{   protected:	int _num;//学号};class Teacher : virtual public Person{   protected:	int _id;//工号};

在这里插入图片描述

B:解决原理

为了方便描述,使用下面的代码作说明

class A{   public:	int _a;};class B : public A{   public:	int _b;};class C : public A{   public:	int _c;};class D : public B, public C{   public:	int _d;};int main(){   	D d;	d.B::_a = 1;	d.C::_a = 2;	d._b = 3;	d._c = 4;	d._d = 5;	return 0;}
  • 继承关系是下面这样的
  • 在这里插入图片描述
  • 内存窗口如下
    在这里插入图片描述

可以发现不采用虚继承的话,对于对象d它的所占的空间就是20个字节。将上述继承改为虚继承,那么它的大小应该变为16个字节,因为只保存了一份a,但是实际查看内存窗口发现它竟然占用了24个字节

在这里插入图片描述
先不要管那多出的八个字节,其余继承关系如下
在这里插入图片描述
可以看出上面的a=1是公共的,b和c都继承了a,那么在赋值时d是如何找到这个位于下方的公共的a呢?上面多出的0055db400055db48很明显是两个指针,称其为虚基表指针,他们分别指向了虚基表,虚基表中存储的是一个偏移量,通过偏移量可以找到a
在这里插入图片描述

在这里插入图片描述

尤其在发生切片操作时,这样的偏移量就能保证赋值时正确的

D dB b=dC c=d

在这里插入图片描述

因此使用虚继承后d.B::a=1这样的操作就是通过偏移量完成的

二:继承总结

(1)继承缺陷

有了多继承,就会出现菱形继承,有了菱形继承就有虚拟继承,因此底层的实现一定会很复杂。所以设计时不建议设计多继承,而且一定不能有菱形继承,否则会出现这样那样的问题

(2)继承与组合

继承class A{   };class B : public A{   }组合class A{   };class B{   	A a;}

public继承是一种is-a的关系,也就是每个子类对象都是一个父类对象(学生也是人),而组合是一种has-a的关系

继承体现的是白箱复用,组合体现的是黑箱复用

在这里插入图片描述

  • 继承允许你根据父类的实现来定义子类的实现。这种方式就是白箱复用,在继承方式中,父类的内部细节对子类可见。继承一定程度上破坏了父类的封装,父类的改变对子类有很大的影响——耦合性太强
  • 组合是继承外的另一种很好的选择,新的更复杂的功能可以通过组合对象获得。对象组合要求被组合的对象具有良好定义的接口,对象的内部细节对“父类”是不可见的,组合类之间没有很强的依赖性——高内聚,低耦合

设计时要优先使用组合而不是继承

  • 看他们更符合is-a关系(比如猫是动物,狗是动物)还是has-a关系(往往是某个对象的属性)
上一篇:8-1:C++继承之对继承的理解和继承的基本使用,继承的格式
下一篇:【README】Linux系统编程必读:本专栏内容提要以及系统调用接口总结

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2025年03月18日 23时47分45秒