当前位置: 首页 > article >正文

【C++】继承(下)

大家好,我是苏貝,本篇博客带大家了解C++的继承(下),如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • 5.继承与友元
  • 6.继承与静态成员
  • 7.复杂的菱形继承及菱形虚拟继承
  • 8.继承的总结和反思
  • 9.笔试面试题

5.继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员,就像父母的朋友不是你的朋友一样
在这里插入图片描述

6.继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。父类静态成员属于当前类,也属于当前类的所有派生类,因此无论派生出多少个子类,都只有一个static成员实例

在这里插入图片描述
在这里插入图片描述

如果我们想知道A及其派生类实例化了多少对象,就可以定义一个static变量

在这里插入图片描述

在这里插入图片描述

7.复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

在这里插入图片描述

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
在这里插入图片描述

多继承的定义如上图的Assistant,只需要在原本继承的基础上加逗号和其它基类即可

菱形继承:菱形继承是多继承的一种特殊情况。比如助教,对老师来说,他是个学生;对学生来说,他是个老师。因此他有学生和老师两个身份,就继承了学生类和老师类
在这里插入图片描述

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性(后面介绍)的问题。
在这里插入图片描述

数据冗余:在Assistant的对象中Person成员会有两份。
二义性:Student和Teacher类都有_name,无法明确知道访问的是哪一个

在这里插入图片描述
在这里插入图片描述

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和 Teacher的继承Person时使用虚拟继承(在继承公共的基类时用虚拟继承),即只保存1份Person的成员,就不会造成数据冗余,二义性也就解决了。需要注意的是,虚拟继承不要在其他地方去使用。

在这里插入图片描述
在这里插入图片描述

在上面这种菱形继承中,虚拟继承用在菱形的腰部B和C,那下图的用在哪呢?也是B和C,virtual放在有公共基类(A)的类中
在这里插入图片描述

虚拟继承解决数据冗余和二义性的原理:
为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

下面是不加虚拟继承的菱形继承
在这里插入图片描述

在这里插入图片描述

我们可以从内存窗口看出,D里面包含2份A的成员,会导致数据冗余和二义性。因为类D先继承B,再继承C,因此D类的对象d中,先出现类B的成员,再是类C的成员,最后是类D的成员。
同理,如果类D先继承C,再继承B,那么D类的对象d中,先出现类C的成员,再是类B的成员,最后是类D的成员。

下面我们加上虚拟继承
在这里插入图片描述
在这里插入图片描述

我们可以从内存窗口看出,D里面只包含1份A的成员,不会导致数据冗余和二义性。而且这时的B和C类的成员中都不包含本来有的A类成员,并且都存储了一个地址(0x00677bdc和0x00677be4,机器是小端存储),我们来看看这2个地址指向的内容

在这里插入图片描述
在这里插入图片描述

原来地址指向的空间(叫虚基表)里存放的是与A的成员的偏移量(还有其它的内容,这里不做介绍)

为什么要存偏移量?在切片的时候有用。将d赋值给类B的对象bb,就要把D类中B类那部分切来赋值过去,可是这里的B类的成员不包括A类的成员,因此B类保存的地址就能找到与A类的成员的偏移量,就能找到A类的成员,才能最终将B类的成员赋值给bb

在这里插入图片描述

多继承本身没有问题,但有多继承就可能导致菱形继承

总结:实践中可以设计多继承,但切记不要设计菱形继承,因为太复杂,容易出现各种问题

问:下面哪个选项是正确的?
在这里插入图片描述

A:p1 == p2 == p3
B:p1 < p2 < p3
C:p1 == p3 != p2
D:p1 != p2 != p3

答案:C
在这里插入图片描述

问:下面哪个选项是正确的?
在这里插入图片描述

A:p1 == p2 == p3
B:p1 < p2 < p3
C:p2 == p3 != p1
D:p1 != p2 != p3

答案:C
在这里插入图片描述

问:下面程序的结果是什么?
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

类D的对象定义时先走初始化列表,再走函数体,所以”class D”一定在最后。先声明的先走初始化列表,D类中先声明的是B类,B继承A,所以A比B更先声明,再声明C,最后D。因此A->B->C->D

8.继承的总结和反思

1、 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
2、 多继承可以认为是C++的缺陷之一,很多后来的语言都没有多继承,如Java。
3、 继承和组合
组合是什么?将一个类(如A)作为成员变量放在另一个类中(如B)
在这里插入图片描述

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

适合is-a的关系,如人和学生,就用is-a。适合has-a的关系,如汽车和轮胎,就用has-a。is-a和has-a都可以,如链表和栈,就用has-a。即优先使用对象组合,而非类继承

继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。

对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

9.笔试面试题

1、 什么是菱形继承?菱形继承的问题是什么?
2、 什么是菱形虚拟继承?如何解决数据冗余和二义性的
3、 继承和组合的区别?什么时候用继承?什么时候用组合?

复用的常见体现:
1、 函数逻辑的复用
2、 模板
3、 继承
4、 组合


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️


http://www.kler.cn/a/531937.html

相关文章:

  • 小程序-基础加强
  • Vue和Java使用AES加密传输
  • C#,入门教程(11)——枚举(Enum)的基础知识和高级应用
  • 算法题(56):旋转链表
  • MySQL子查询
  • 排序算法--桶排序
  • 吴恩达深度学习——卷积神经网络基础
  • GESP2023年12月认证C++六级( 第三部分编程题(1)闯关游戏)
  • PyQt4学习笔记1】使用QWidget创建窗口
  • Kubernetes服务网格实战:从理论到落地
  • 经典本地影音播放器MPC-BE.
  • 动手学深度学习-3.1线性回归 问题汇总
  • 指导初学者使用Anaconda运行GitHub上One - DM项目的步骤
  • Python玄学
  • 【Jax和Flax介绍】
  • redis实际开发应用简单实现
  • c语言练习题【数据类型、递归、双向链表快速排序】
  • 14-9-3C++STL的set容器
  • C语言按位取反【~】详解,含原码反码补码的0基础讲解【原码反码补码严格意义上来说属于计算机组成原理的范畴,不过这也是学好编程初级阶段的必修课】
  • 安装nvm后报错
  • 计算机网络中常见高危端口有哪些?如何封禁高危端口?
  • nvm的安装和使用
  • VSCode源码分析参考资料
  • Java自定义IO密集型和CPU密集型线程池
  • OpenGL学习笔记(七):Camera 摄像机(视图变换、LookAt矩阵、Camera类的实现)
  • 【6. 深入理解 C++ 中的常量】