【C++进阶】1. 继承

在这里插入图片描述

1. 继承的概念及定义

1.1继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
在这里插入图片描述

1.2 继承定义

1.2.1 定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类
在这里插入图片描述

1.2.2 继承关系和访问限定符

在这里插入图片描述

1.2.3 继承基类成员访问方式的变化

在这里插入图片描述
总结:

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。
    这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
    在这里插入图片描述
  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。
    可以看出保护成员限定符是因继承才出现的
    在这里插入图片描述
  3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。
    基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
    在这里插入图片描述
  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承。
    因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
    (这里其实也就是C++实现复杂的地方之一,因为C++是最开始实现的面向对象的语言,大佬们是希望实现父子类之间成员的类型转换,但是在实际场景中基本没有用武之地…)
    在这里插入图片描述

2. 基类和派生类对象赋值转换 (重点)

  1. 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割
    寓意把派生类中父类那部分切来赋值过去。
  2. 基类对象不能赋值给派生类对象
  3. 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。
    这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。(ps:这部分内容将在多态当中进行介绍)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    这里的具体问题等多态部分进行具体分析:
    现在只需要记住子可以给父,但是父不可以给子

3. 继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。(容易混淆)

父子类当中的同名成员 – “隐藏”
在这里插入图片描述
父子类当中的同名函数 – “隐藏”
在这里插入图片描述

4. 派生类的默认成员函数

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

默认构造函数

在这里插入图片描述

拷贝构造函数

在这里插入图片描述

赋值重载

在这里插入图片描述

析构函数

在这里插入图片描述
在这里插入图片描述
说明析构函数不是这么实现滴!
针对析构函数的继承,C++大佬提出以下观点:
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。
因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序

(也就是说不再需要我们显示调用,这样才能确保子类在父类前进行析构,如果让我们自己实现的话是无法保证析构顺序滴!)
在这里插入图片描述
所以,析构函数如上实现(因为student类当中没有需要释放的资源,所以析构函数不需要写啥[甚至可以不用实现] )
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个要等学习了多态之后才能了解)。
那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

总结:
派生类相较于正常类来说多了基类对象
0.基类对象 – 调用父类对应的函数进行初始化/清理/拷贝/赋值
而派生类当中的自定义类型、内置类型
仍然还是调用对应的方法(和正常类相同)

5. 继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
在这里插入图片描述
友元关系能不用就不用,友元关系破坏封装性

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
在这里插入图片描述
C++所特有的多继承(Java没有) 有多继承就可能会引发菱形继承的问题
菱形继承:菱形继承是多继承的一种特殊情况。
在这里插入图片描述
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
在Assistant的对象中Person成员会有两份。
在这里插入图片描述
在这里插入图片描述
解决方案2:虚拟继承
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。
需要注意的是,虚拟继承不要在其他地方去使用。
在这里插入图片描述

分析: 虚继承是如何解决菱形继承的二义性(笔试面试考点)

如何回答:

  1. 从语法上来说,在腰部位置进行虚继承
  2. 从对象模型上来说,将虚基类放到底部 在切片等场景下为了找虚基类的位置
    在原来存储虚基类的位置存储了虚基表的指针
    找到虚基表拿到虚基类的偏移量 就可以找到虚继类
    在这里插入图片描述
    在这里插入图片描述
    采用虚继承的方式会一定程度上降低效率,因为需要根据地址找到距离虚基类对象的偏移量,然后在该地址加上偏移量最终确定基类对象的位置 需要间接计算(普通继承就是直接访问即可)
    从上述的例子当中反应 感觉虚继承并没有在空间性能上的提升,反而因为存储偏移量的缘故使得空间开辟更大
    但是其实还是节省了空间的,加入虚基类(A) 占用100个字节 那么多继承(B,C)就是200个字节 而虚继承就只需要加上这点偏移量的空间即可

8.继承的总结和反思

  1. 很多人说C++语法复杂,其实多继承就是一个体现有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。
  3. 继承和组合
    public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
    组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
    优先使用对象组合,而不是类继承 。
    继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
    实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43468.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习之主成分分析(Principal Component Analysis)

1 主成分分析介绍 1.1 什么是主成分分析 主成分分析(Principal Component Analysis)简称PCA,是一个非监督学习的机器学习算法,主要用于数据的降维,对于高维数据,通过降维,可以发现更便于人类理…

【stable diffusion】保姆级入门课程01-Stable diffusion(SD)文生图究竟是怎么一回事

目录 学前视频 0.本章素材 1.什么是文生图 2.界面介绍 2.1切换模型的地方 2.2切换VAE 2.3功能栏 2.4提示词 1.提示词的词性 2.提示词的语法 3.提示词的组成 4.提示词的权重调整 2.5参数调整栏 1.采样方法 2.采样迭代步数 3.面部修复 4.平铺图 5.高清修复 6.…

Linux系统入门之-系统编程【open、close函数】

继上一篇环境配置后就正式开始系统编程 RK3568开发板入门之-tftp&nfs的配置 open的使用,使用之前可以先在Ubuntu下查看帮助,了解open的使用和语法,如下: man 2 open对于open函数 *pathname:要打开的文件路径 f…

Linux安装JDK、Redis、MySQL、RabbitMQ、Minio、Nginx.......

文章目录 一、环境准备二、安装JDK三、安装MySQL四、安装Redis三、安装RabbitMQ四、安装Minio五、安装Nginx特殊情况处理Centos7挂载磁盘服务器时间同步MySQL数据库时间同步安装解压软件修改数据库SQL模式 一、环境准备 下载镜像源 中科大镜像源下载至/opt目录下修改yum源为中…

flask 页面新增文件,存在重复文件时,返回错误消息

(40条消息) flask 读取文件夹文件,展示在页面,可以通过勾选删除_U盘失踪了的博客-CSDN博客 项目结构 这是一个基本的Flask应用程序,主要有两个路由,一个是index,用于显示所有存在的文件以及用于删除已选的文件&#…

Java使用 java.util.regex.Pattern 正则表达式校验参数值是否规范

场景: java中我们可以利用 Pattern 注解对某个入参进行规则校验,但有些特殊参数在接口入口处不方便校验,需要在代码中校验 一、使用 Pattern 注解校验 Pattern(regexp "^[a-zA-Z0-9]$", message "xxx号限输入字母、…

4.1 Bootstrap UI 编辑器

文章目录 1. Bootstrap Magic2. BootSwatchr3. Bootstrap Live Editor4. Fancy Boot5. Style Bootstrap6. Lavish7. Bootstrap ThemeRoller8. LayoutIt!9. Pingendo10. Kickstrap11. Bootply12. X-editable13. Jetstrap14. DivShot15. PaintStrap 以下是 15 款最好的 Bootstrap…

百度文心一言文心千帆大模型 ERNIE-Bot-turbo调用示例(golang版本)

百度的文心一言推出来也有一段时间了,但是接口部分一直没有公开,需要进行申请 最近,有朋友提供了文心千帆大模型的api权限,拿到了必须的参数,现在就来测试一下 下面是使用golang封装的文心千帆 ERNIE-Bot-turbo模型的调…

C++面向对象程序设计-基础入门(超详细)

目录 一、c概述 二、初识c 1、第一个c程序 2、c面向对象的三大特性(重要) 三、作用域运算符:: 1、使用关键字namespace创建一个命名空间 2、命名空间只能定义在全局 3、 命名空间嵌套 4、随时将新的成员加入命名空间 5、命…

DXFReader.NET 2023 Crack

DXFReader.NET 是一个 .NET 组件,允许直接从 AutoCAD 图形文件格式 DXF(也称为图形交换格式)查看、操作和打印。 DXFReader.NET 之 DXF 是 Drawing eXchange Format 的首字母缩写。DXF 是图形文件内容的复制,支持将文件从一个 CA…

picgo Request failed with status code 404

今天写picgo的时候,出现了一个错误,如何解决: 这里是repo的配置出现了问题,不过我的是因为粗心,把master写成了mater,emmmm 这里的repo要跟仓库的地址相同就是这一块:把这一块填到repo就行 然…

算法之图论

定义 图通常以一个二元组 G<V, E>表示&#xff0c;V表示节点集&#xff0c;E表示边集。节点集中元素的个数&#xff0c;称为图的阶。 若图G中的每条边都是没有方向的&#xff0c;称为无向图&#xff1b;每条边是由两个节点组成的无序对&#xff0c;例如节点V1和节点V2之…

论文阅读:矩阵乘法GEMM的cache优化,子矩阵的切分方法Anatomy of High-Performance MatrixMultiplication

矩阵乘法优化的知名论文goto paper&#xff1a; 矩阵乘法的优化需要将矩阵切分成子矩阵&#xff0c;用子矩阵相乘的结果组合为原矩阵相乘的结果&#xff1a; 上图是拆分矩阵的方法&#xff0c;M表示矩阵&#xff0c;X方向和Y方向的两个维度都是未知的。P表示横条或竖条&#x…

前端监控一vue指令实现埋点

前端监控一vue指令实现埋点 https://v2.vuejs.org/v2/guide/custom-directive.html 自定义指令 需要在main.js中执行 import Vue from vue // 自定义埋点指令 Vue.directive(track, {//钩子函数&#xff0c;只调用一次&#xff0c;指令第一次绑定到元素时调用。在这里可以…

Linux 下 nc 发送接收 udp、tcp数据

nc&#xff0c;全名叫 netcat&#xff0c;它可以用来完成很多的网络功能&#xff0c;譬如端口扫描、建立TCP/UDP连接&#xff0c;数据传输、网络调试等等&#xff0c;因此&#xff0c;它也常被称为网络工具的 瑞士军刀 。 一、只服务端使用nc 备注&#xff1a;这种方式只能发…

新能源汽车交流充电桩CP信号详解

随着新能源汽车的推广&#xff0c;交流充电桩迎来了巨大的市场需求&#xff0c;人们对车辆充电的便利性、安全性有着越来越高的要求。CP信号主要用于交流充电桩&#xff0c;充电桩和汽车之间只能通过CP信号进行通讯&#xff0c;判断、控制充电电流和状态。 汽车充电桩CP信号…

124.【SpringBoot 源码刨析C】

SpringBoot源码刨析C (三)、SpringBoot核心功能2.Web4.数据响应与内容协商(1).响应JSON&#xff08;1.1&#xff09;jackson.jarResponseBody&#xff08;1.1.1&#xff09;、返回值解析器&#xff08;1.1.2&#xff09;、返回值解析器原理 (1.2).SpringMVC到底支持哪些返回值(…

【STL】模拟实现简易 list

目录 1. 读源码 2. 框架搭建 3. list 的迭代器 4. list 的拷贝构造与赋值重载 拷贝构造 赋值重载 5. list 的常见重要接口实现 operator--() insert 接口 erase 接口 push_back 接口 push_front 接口 pop_back 接口 pop_front 接口 size 接口 clear 接口 别…

Window环境RabbitMq搭建部署

Erlang下载安装及配置环境变量 下载erlang&#xff0c;原因在于RabbitMQ服务端代码是使用并发式语言Erlang编写的 Erlang下载 Erlang官网下载&#xff1a; http://www.erlang.org/downloads Erlang国内镜像下载&#xff08;推荐&#xff09;&#xff1a; http://erlang.org/d…

旧版Xcode文件较大导致下载总是失败但又不能断点续传重新开始的解决方法

问题&#xff1a; 旧版mac下载旧版Xcode时需要进入https://developer.apple.com/download/all/?qxcode下载&#xff0c;但是下载这些文件需要登录。登录后下载中途很容易失败&#xff0c;失败后又必须重新下载。 解决方案&#xff1a; 下载这里面的内容都需要登录&#xff0…