【C++】-----继承(复杂的多继承及虚拟继承)

news2024/9/22 9:38:52

目录

前言

一、多继承

认识

继承顺序

二、菱形继承

三、菱形虚拟继承(重难点)

 认识

底层原理(细致)

四、继承与组合

五、总结


前言

在前面我们所举的例子都是单继承,就是一个子类只有一个直接父类的关系!但是这还不是C++继承的结束,只是开始,下面来了解了解C++中比较复杂的继承关系!!!

一、多继承

认识

  • 顾名思义,就是一个子类有两个或多个直接父类的继承关系

如下: 

 

Son2这个类既有Son1特征也有Father的特征,就像有的水果同时也是蔬菜一样!! 从这个角度看,多继承的存在是合理的。。。但是会引发一个BUG---菱形继承(见下文讲解)!

继承顺序

多继承的对象模型中,一定要注意父类的继承顺序,顺序不同,在子类对象模型存储的位置也不同。

举例:

如上图的子类Son2,其对象模型为:

对于上述模型通常会有以下思考问题

Son2 s;

Son2* p1 = &s;
Son1* p2 = &s;
Father* p3 = &s;

问针对上图的继承关系p1、p2、p3的关系是怎样的?

对于上图三个指针的关系是:p1=p2!=p3 

若上述Son2的继承关系改成如下形式,则对象模型也会方式一定的变化!

//Sons继承关系
class Son2:public Father,public Son1
{}

此时Son2的对象模型变为:

 

那么此时三个指针的关系就是:p1 =p3 !=p2

二、菱形继承

菱形继承是多继承的一种特殊形式,也可以理解为BUG的意思!

如下:

那么子类的D的对象模型就会变成这样子!

 

从上图可以看出菱形继承存在的问题:

①数据二义性 

不知道要访问的是B类中的_name,还是C类的,造成二义性!!

这个问题的解决方案就是显示的指定是哪个父类的成员,如下:

②数据冗余

 所谓数据冗余,就是D的对象模型中存在两份_name,这个是必然的。怎么解决?虚拟继承就可以!

三、菱形虚拟继承(重难点)

 认识

 虚拟继承可以很好的解决菱形继承的二义性和数据冗余的问题。写法就是在腰部加上virtual关键字,如上述的继承关系,在B和C的继承A时使用虚拟继承。即在继承有公共父类的时候使用!

注意:虚拟继承不要在其他地方去使用!

底层原理(细致)

 先来对比一下普通的菱形继承和虚拟继承的内存模型!

普通菱形继承内存模型如下:

因此对于普通的菱形继承要访问_a,需要指明是哪个类!也可以看到数据冗余了B和C中都存同一个_a。

菱形虚拟继承的对象内存模型如下:

如果上图的内存模型可以看出:d的对象模型组成中,将A的成员变量_a放在最下面的位置,这个_a同时属于B和C的,你看到的是2,因为小编调试结束了,实际上调试过程中,是从1变到2的!这更加说明了共用的是一个_a!这样就是无需指明是具体的哪个类,很好的解决二义性和数据冗余的问题!

既然共用,那要B和C怎么去找公共的A呢?

大家可以看到B和C的内存模型分别有两个地址/指针:0x005c7b48 和 0x005c7b54(小端存储)

这两个指针实际指向的各自的两个表,叫做虚基表,指针叫做虚表指针,这个虚表里面存的是偏移量。而通过偏移量就能够找到_a了。

如:在C内存模型中0x005c7b54这个虚表指针,该虚表指针在d对象模型中的地址是0x008ff6e0,因为所指向的虚表中存的偏移量是0x0000000c,0x0000000C+0x008ff6E0=0x008ff6EC,最终得到的地址正是_a的位置。。。

B和C在内存中存储的位置又不一样,所以就需要加上偏移值!

为什么B和C要找属于自己的_a?

因为为了赋值场景呀,子类给父类赋值,不就是将属于父类的那部分切出去吗?那就要找出B/C成员中的A才能赋值过去啊!!

D d;
B b = d;
C c = d;

还需注意一个问题:使用虚拟继承时,被加上virtual关键字的类的对象模型也会改变。如上述B和C类的对象模型 会和d对象模型保持一致,都是会将_a放在最下面。。

四、继承与组合

  • 继承是is -a的关系,就是每个子类对象都是一个特殊的父类对象。
  • 组合是has -a的关系,每一个子类对象都有一个父类对象。。
//组合,将在B中包含一个A类型的成员
class A
{
private:
	int _a;
};
class B
{
private:
	A _aa;
	int _b;
};
  •  继承和组合都是复用的方式,实际中优先使用组合,而不是继承!
  • 继承中,父类的内部细节对子类可见,“白箱复用”。一定程度上破坏了父类的封装,父类的改变,对子类影响大。这就导致代码之间的依赖性强,耦合度高。。
  • 组合是继承之外的另外一种选择,组合对象的内部细节不可见,也称“黑箱复用”。代码之间依赖性并不强,你的改变并不会影响我,耦合度低。所以尽量多的去使用组合。

五、总结

①有了多继承就会又菱形继承,有了菱形继承就会有菱形虚拟继承,底层就很复杂。实际中可以使用多继承,但是切记一定不要设计出菱形继承。否则在复杂度及性能上都会存在问题!

②多继承可以认为是C++的缺陷之一,后来的很多语言就没有多继承。如java。所以说C++语法复杂,多继承就是一大体现!


好了,老铁们今天的分享就到这,如果觉得对你有用,欢迎三连,你的支持就是我前进的动力!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Manim在图形和坐标轴上画线条

用Manim在图形和坐标轴上画线条 .画图像函数的切线 angle_of_tangent(x, graph, dx1e-08) angle_of_tangent(x, graph, dx1e-08)是 Manim 中用于计算图形在给定点的切线角度的函数。以下是对该函数参数的解释: 参数说明 x: 这是你想要计算切线角度的 x 坐标。在…

C++进阶之C++11

个人主页:点我进入主页 专栏分类:C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 C进阶 欢迎大家点赞,评论,收藏。 一起努力,一起奔赴大厂 目录 一.列表初始化 1.1一切皆可用列表初始化 1.2init…

代码随想录 day 29 贪心

第八章 贪心算法 part03 134. 加油站 本题有点难度,不太好想,推荐大家熟悉一下方法二 https://programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html 135. 分发糖果 本题涉及到一个思想,就是想处理好一边再处理另一边,不…

MySQL基础操作全攻略:增删改查实用指南(上)

本节目标: CRUD : Create, Retrieve , Update , Delete 新增数据 查询数据 修改数据 删除数据 1. CRUD 注释:在 SQL 中可以使用 “-- 空格 描述 ” 来表示注释说明 CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、…

什么是 IDR —— Linux 内核中的一种整数管理机制

文章目录 1 什么是 IDR1.1 IDR 的设计目的 2 IDR 的结构和实现2.1 核心数据结构2.2 常用操作2.2.1 分配 ID2.2.2 查找指针2.2.3 删除映射 2.3 IDR 的优点 3 Linux 内核中的整数 ID3.1 作用3.2 常见的整数 ID 示例 4 为什么要将整数 ID 与指针关联4.1 举例说明4.2 好处4.3 示例代…

学习笔记-Cookie、Session、JWT

目录 一、验证码的生成与校验 1. 创建生成验证码的工具类 2. 写一个 Controller 3. 实现验证码验证 1. 获取验证码 2. 验证码请求过程 3. 验证码的校验 4. 原理说明 5. 验证 6. 总结 二、JWT登录鉴权 1. 为什么要做登录鉴权? 2. 什么是 JWT 3. JWT相比…

MATLAB优化模型(2)

一、前言 在MATLAB中实现动态规划、图论、网络流模型(如最短路、最大流、最小生成树)的优化模型,可以通过多种方法完成,但通常会依赖于MATLAB内置的函数或工具箱,比如Optimization Toolbox、Graph Theory Toolbox等。以…

Python 实现股票指标计算——SKDJ

SKDJ (Stochastic KDJ) - 慢速随机指标 1 公式 LOWV:LLV(LOW,N); HIGHV:HHV(HIGH,N); RSV:EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M); K:EMA(RSV,M); D:MA(K,M); 2 数据准备 我们以科创50指数 000688 为例,指数开始日期为2019-12-31,数据格式如下&#…

Leetcode 第 135 场双周赛题解

Leetcode 第 135 场双周赛题解 Leetcode 第 135 场双周赛题解题目1:3222. 求出硬币游戏的赢家思路代码复杂度分析 题目2:3223. 操作后字符串的最短长度思路代码复杂度分析 题目3:3224. 使差值相等的最少数组改动次数思路代码复杂度分析 题目4…

SQL注入 报错注入、文件上传、布尔盲注、时间盲注

第7关 文件上传 ---面试官常问 1、MySQL上传shell的满足条件 如果面试官问你如何通过MySQL向网站上传一个shell脚本或者其他语言的一些脚本 ---就可以通过outfile导出的方式进行上传; outfile导出的前提条件:1、必须知道网站的物理路径&#xf…

Java每日一练_模拟面试题2(循环依赖)

一、啥事Spring里面的循环依赖 SpringBoot 循环依赖通常发生在两个或多个Bean相互依赖对方时,例如:A依赖B,同时B也依赖A。 二、如何解决? 解决方案: 构造器注入:如果循环依赖发生在构造器中,S…

[YashanDB认证]YashanDB个人版安装

为什么选择YashanDB? 崖山数据库系统YashanDB是深圳计算科学研究院完全自主研发设计的新型数据库系统,经工信部下属机构权威检测,内核代码自主率100%。在经典数据库理论基础上,融入原创的有界计算理论、近似计算理论、并行可扩展理论和跨模融…

Taro学习记录(具体项目实践)

一、安装taro-cli 二、项目文件 三、项目搭建 1、Eslint配置 在项目生成的 .eslintrc 中进行配置 {"extends": ["taro/react"], //一个配置文件,可以被基础配置中的已启用的规则继承"parser": "babel/eslint-parser…

荒原之梦考研:专科考研成功的可能性大吗?

专科还是本科不是决定考研能否成功的关键因素,决定考研能否成功的关键因素是自己是否有清晰的规划、是否有足够的专注能力,以及是否能够吃得了考研的“苦”。 首先要有清晰的规划,比如说,不是我们每个人足够努力就都能考上 TOP1 …

electron-updater实现electron全量更新和增量更新——主进程部分

同学们可以私信我加入学习群! 正文开始 前言更新功能所有文章汇总一、更新插件选择二、在main.js中引入我们的更新模块三、更新模块UpdateController.js暴露的方法checkUpdate四、更新模块UpdateController.js中的监听4.1监听是否有新版本需要更新?4.2 监…

红黑树与平衡二叉树的相同之处与不同之处

红黑树很多资料上写的非常繁杂,初次接触真的难以理解。写本文也就是为了记录一些思考和想法,并不会记录如何使用代码实现。 不记录代码还有个原因:黑红树的算法就是根据各种情况进行一些操作,情况很复杂,分插入的和删…

数据结构 二叉树和堆总结

树 概念 树是一种层次结构非线性的数据结构,其是由节点和边组成,可以用来表示层次关系的数据。 树的相关概念 节点:树的基本组成单位,每个节点都包含数据,同时与其他节点相互连接根节点:树的顶层节点&…

SpringBoot_第十一章(Thymeleaf模板引擎)

目录 1:什么是Thymeleaf模板引擎 2:springboot怎使用Thymeleaf 2.1:导入pom文件 2.2:查看ThymeleafAutoConfiguration 3:Thymeleaf核心语法 4:使用Thymeleaf 5:具体语法练习 1&#xff1a…

数据集划分方法

数据集划分是机器学习和数据科学中的一个重要步骤,主要目的是为了确保模型的有效性和可靠性。 留出法(简单交叉验证) 将数据集划分为互斥的子集:训练集和测试集。 训练集: 用于训练模型。 测试集: 用于评估模型的性能和验证其准确…

图神经网络揭秘:视觉和实用指南

目录 一、说明 二、图如何网络化? 三、你需要知道的 3.1 进入图神经网络 3.2 消息传递 3.3 我们如何处理最终的向量表示? 四、图神经网络,总结 4.1 为什么选择图形神经网络? 4.2 简而言之 一、说明 了解图神经网络的世界&#xff…