C++中的继承(二)
文章目录
- 前言
- 多继承
- 虚继承
- 虚继承的底层
- 组合
前言
上一篇文章我们C++的正常继承其实已经讲完了,但是后面还有一个大坑。
实际当中继承有单继承和多继承。
单继承就是直接继承一个类。
只有一个直接父类的就叫做单继承。
如果是单继承那就比较简单。
现实世界除了有单继承还有多继承。
多继承
多继承就是我一个类我具备另外两个类的特征。
单继承就是一个类只具备另外一个类的特征。
现实世界当中有什么东西需要具备两个特征都继承一下呢?
比如:有没有一种物种既具有水果的特征,也具有蔬菜的特征?
番茄。
多继承很重要,它能够更好的描绘这个世界。
所以多继承看起是很合理的,但是它有一个大坑。
多继承就可能会导致这种菱形继承。
菱形继承会有什么样的问题呢?
他会导致对象里面有两份人的信息。
这样你就不知道要访问从学生那里继承来的呢,还是从老师那里继承来的呢?
指定访问是能解决二义性的
但是这样很不合理的。
首先名字肯定有一个正式的名字比较合理,另外如果有其他的一些信息,
你肯定会觉得很冗余,比如
数据冗余的本质是空间浪费
所以不要搞出菱形继承,非常非常坑。
虚继承
菱形继承怎样解决数据冗余和二义性呢?
这里引入了一个新的东西,虚继承。
它们都变成了同一个。所以也得出一个结论,监视窗口看到的不一定是真实的,
它们都是被处理过的。
从实际的角度,可以用多继承,但是不要用菱形继承。
但是从学习的角度,我们还得学一下这个菱形继承。
这样是不是菱形继承?
是的,它都有数据冗余和二义性。
虚继承的底层
虚继承是如何解决数据冗余和二义性的?
虚继承虽然解决了数据冗余和二义性,但是这个过程是很难看的。
监视窗口已经看不出它最真实的面目。它的底层不是这样的。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:string _name; // 姓名
};
class Student : virtual public Person
{
protected:int _num; //学号
};
class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
void Test()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;a._name = "peter";// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}int main()
{Test();return 0;
}
这里虽然有三个_name,但是每个_name都是一样的。
它的底层到底是怎么样呢?
物理上它的底层到底是怎么样呢?这里要换一个角度去看,
监视窗口已经看不到真实的东西,我们这里看一下内存窗口,内存窗口是真实的,不加修饰的。
我们这里用一个简化的类模型,方便我们看。
class A
{
public:int _a;
};
// class B : public A
class B : virtual public A
{
public:int _b;
};
// class C : public A
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
ABCD依次构成继承。
看它的底层是什么样的,我们先看不看虚继承,就看菱形继承。
接下来我们看虚继承。
对象模型相比刚才已经发生了本质的变化。
注意看这里面还有我们不认识的两个东西,这些东西是什么?
00 aa cd 8c
00 aa cb ac
这两个有点像指针,而且距离也不远
它们具体是什么呢,我们再用一个内存窗口观察一下。
注意这是小端,然后把它调成4,因为编译器是32位的,指针也是32位的,并且存的是整型,正好对齐方便观察。
这是怎么回事,这个地址指向的空间是个0, 并且下面有一个值。
这个值到底是什么?
所以这两个值是偏移量,也是相对距离。
之前是B里有一个a,C里也有一个a,那这就造成数据冗余和二义性。
现在把一个a放到公共的空间去,然后通过偏移量去找。
那现在问题来了,为什么不直接存a的地址呢?
直接存a的地址,这样不是更好吗?
大家注意,这个偏移量它没有存到第一个位置,这个位置是空出来的,为以后的
多态做准备。
如果这里存a地址,只解决了一个问题。而如果这个地方存放一个指针,指向一个表,这张表
可以存很多其他信息。
什么情况会涉及刚才这样一块问题呢?
以前的赋值兼容转换直接切割就可以了,现在直接切父类没毛病,但是不完整,还有一个a.
这个a在哪呢,在切的时候就涉及一个问题,找到对应的a。
怎么找呢?拿到对应的偏移量,然后计算才能找到a.
第二种就更复杂了。(这里还是比较难的)
ptrc指向哪里?
不是指向最开始的地方,多继承指针会发生偏移。
虚继承还有更复杂的问题。b对象的对象模型是什么样的?
按照我们以前的理解,b里面有一个_a,有个一个_b,现在实际并不是这样。
虚继承影响了这块。它要保持一致。
也就意味着这里面有两种情况,这两个代码看起来一样,实际上跑起来天差地别。
一个指向d对象,一个指向b对象。它们的偏移量也是不一样的。
但是它们的汇编指令是一样的。
它们去访问a是一样的,都是找第一个位置的地址。拿这个地址找到指向的表,
找到偏移量,然后计算找到a.
验证一下上面说的
一个是普通赋值,一个是切片
这样它的模型就对上了,它不需要区分是子类对象还是父类对象,它的动作是一样的。
汇编指令是一样的。
虚继承不是要解决数据冗余的问题吗?怎么还变大了?
这块变大了,是因为a太小了。它要解决数据冗余是有成本的,增加两个指针。
但是这个成本是固定的,而这个数据的大小是不确定的。
为什么不需要考虑指向的空间?
一个类可能定义很多很多对象,每个对象空间都要多8个字节,
但是这个空间不是每个对象独立的,是共同分担的,因为它们的偏移量是不变的。
真正的消耗并不在这里。
自己可以单独去验证一下。
小问题
1.如果A有多个成员,需不需要增加指针?
不需要,首先偏移量不会变,并且它是按照声明顺序去访问的。
内存对齐并不会影响这个。
举个例子。ptr是如何访问这些成员的?
这里也有内存对齐。编译器也是根据内存对齐的规则去算的。
写编译器的人真的是高手中的高手
虚继承是有一定的效率损失的。
在实际当中我们不要去玩菱形继承,效率上有损失而且出问题了很难分析。
看一下下面这道题,结果是什么,看一下你还想不想玩菱形继承。
这道题也没什么,就是在虚继承的基础上加入了构造函数。
这里打印顺序是什么?
这里面调用三次A的构造函数,难道打印了三次A吗?
打印三次A就意味着A被初始化三次。看起来好像这样。
编译器肯定做了很多特殊处理。
A的构造函数实际应该是调用一次,因为只有一份A.
现在还有一个问题,调的这个A,是B里的A,还是C里的A,还是单独的A?
肯定是D里面单独调用这个A最好。外面单独去搞更好。
这里面的运行顺序是怎样的呢?
为什么先调A?
因为初始化列表初始化的顺序跟出现的顺序无关,跟声明的顺序有关,
谁先声明谁先初始化。
注意,谁先被继承谁就先声明
这样出题难度更大。
实际结果没有变。
组合
什么是组合?举个例子。
D想复用C,可以像上面这样复用。
单从关系来说AB的继承关系更紧密一些,还是CD的组合关系更紧密一些?
也就是耦合度,继承的耦合度更高一些,为什么?
实际当中组合更好
我们之前的适配器就用了组合。
为什么还要用继承?
有些关系适合继承那就用继承。另外多态的基础必须是继承。
两个都可以那就用组合,适合用继承就用继承,
适合用组合就用组合。
容器里面有没有迭代器?
容器里面是没有迭代器的,除了vector.
容器只是通过begin()去获取那个位置的迭代器,并不是里面有。
相关文章:

C++中的继承(二)
文章目录 前言多继承虚继承虚继承的底层组合 前言 上一篇文章我们C的正常继承其实已经讲完了,但是后面还有一个大坑。 实际当中继承有单继承和多继承。 单继承就是直接继承一个类。 只有一个直接父类的就叫做单继承。 如果是单继承那就比较简单。 现实世界除了有…...
sklearn多项式回归和线性回归
什么是线性回归? 回归分析是一种统计学方法,用于研究自变量和因变量之间的关系。它是一种建立关系模型的方法,可以帮助我们预测和解释变量之间的相互作用。 回归分析通常用于预测一个或多个因变量的值,这些因变量的值是由一个或多…...

Postman报:400 Bad Request
● 使用Postman发送Post请求报400,入参为JSON; 二、分析 1、Postman请求并没有请求到后台Api(由于语法错误,服务器无法理解请求); 2、入参出错范围:cookie、header、body、form-data、x-www-f…...
apache poi_5.2.5 实现表格内某一段单元格的复制
apache poi_5.2.5 实现表格内,某一段单元格的复制。 实现思路 1.定位开始位置 2.从开始位置之后,在行索引集合中添加行索引下标 3.截至到结束位置。 4.对行索引集合去重,并循环行索引集合 5.利用XWPFTableRow对像的getCtRow().copy()方法&a…...
Oracle重建索引详解
更新:2023-05-17 18:08 一、Oracle重建索引命令 Oracle重建索引可以通过ALTER INDEX命令来完成。下面是示例代码: ALTER INDEX index_name REBUILD [PARAMETERS];其中,index_name是需要重建的索引名称,PARAMETERS是可选的重建参…...

众和策略证券开户首选:股票增持是好还是坏?大股东增持规定?
股票增持是好仍是坏? 股东增持在一定程度上反映股东对个股比较看好,大量的买单,增加了市场上的多方力气,会推动股价上涨,是一种利好消息。 一般大股东会增持可能是上市公司运营成绩较好,具有较大的发展前…...

UE4移动端最小包优化实践
移动端对于包大小有着严苛的要求,然而UE哪怕是一个空工程打出来也有90+M,本文以一个复杂的工程为例,探索怎么把包大小降低到最小。 一、工程简介 工程包含代码、插件、资源、iOS原生库工程。 二、按官方文档进行基础优化 官方文档 1、勾选Use Pak File和Create comp…...

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成
一、插件 Settings... 1.1 File -- Settings 1.2 Settings -- Plugins 1.2.1 搜索框,也可以直接搜索 1.3 Plugins -- 【输入 & 搜索】mybatis 1.3.1 插件不同功能介绍 1.3.2 翻译如下 1.4 选中 Update,更新下 1.4.1 更新中 1.4.2 Restart IDE 1…...

java开发面试:常见业务场景之单点登录SSO(JWT)、权限认证、上传数据的安全性的控制、项目中遇到的问题、日志采集(ELK)、快速定位系统的瓶颈
单点登录(SSO) 单点登录,Single Sign On(简称SSO),只需要登录一次,就可以访问所有信任的应用系统。 如果是单个tomcat服务,session可以共享,如果是多个tomcat,那么服务s…...

Java网络编程原理与实践--从Socket到BIO再到NIO
文章目录 Java网络编程原理与实践--从Socket到BIO再到NIOSocket基本架构Socket 基本使用简单一次发送接收客户端服务端 字节流方式简单发送接收客户端服务端 双向通信客户端服务端 多次接收消息客户端服务端 Socket写法的问题BIO简单流程BIO写法客户端服务端 BIO的问题 NIO简述…...

ARM GIC(三) gicv2架构
ARM的cpu,特别是cortex-A系列的CPU,目前都是多core的cpu,因此对于多core的cpu的中断管理,就不能像单core那样简单去管理,由此arm定义了GICv2架构,来支持多核cpu的中断管理 一、gicv2架构 GICv2,支持最大8个core。其框图如下图所示: 在gicv2中,gic由两个大模块组成: …...

第4章Netty第二节入门案例+channel,future,promise介绍
需求 开发一个简单的服务器端和客户端 客户端向服务器端发送 hello, world服务器仅接收,不返回 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.39.Final</version> </d…...

【论文笔记】3D Gaussian Splatting for Real-Time Radiance Field Rendering
原文链接:https://arxiv.org/abs/2308.04079 1. 引言 网孔和点是最常见的3D场景表达,因其是显式的且适合基于GPU/CUDA的快速栅格化。神经辐射场(NeRF)则建立连续的场景表达便于优化,但渲染时的随机采样耗时且引入噪声…...
【生物信息学】层次聚类过程
文章目录 一、理论二、实践过程1过程2 一、理论 层次聚类是一种基于树状结构的聚类方法,它试图通过在不同层次上逐步合并或分裂数据集来构建聚类结构。这个树状结构通常被称为“树状图”(dendrogram),其中每个节点代表一个数据点或…...

变分自动编码器【03/3】:使用 Docker 和 Bash 脚本进行超参数调整
一、说明 在深入研究第 1 部分中的介绍和实现,并在第 2 部分中探索训练过程之后,我们现在将重点转向在第 3 部分中通过超参数调整来优化模型的性能。要访问本系列的完整代码,请访问我们的 GitHub 存储库在GitHub - asokraju/ImageAutoEncoder…...

KnowLM知识抽取大模型
文章目录 KnowLM项目介绍KnowLM项目的动机ChatGPT存在的问题 基于LLama的知识抽取的智析大模型数据集构建及训练过程预训练数据集构建预训练训练过程指令微调数据集构建 指令微调训练过程开源的数据集及模型局限性信息抽取Prompt 部署环境配置模型下载预训练模型使用LoRA模型使…...

MySQL数据库 索引
目录 索引概述 索引结构 二叉树 B-Tree BTree Hash 索引分类 索引语法 慢查询日志 索引概述 索引 (index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种…...
ES 错误码
2xx状态码(如200)表示请求成功处理,并且不需要重试。 400状态码表示客户端发送了无效的请求,例如请求的语法有误或缺少必需的参数。在这种情况下,重试相同的请求很可能会导致相同的错误。因此,应该先检查并…...

听GPT 讲Rust源代码--src/tools(18)
File: rust/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs 在Rust源代码中的from_comment.rs文件位于Rust分析器(rust-analyzer)工具的ide-ssr库中,它的作用是将注释转换为Rust代码。 具体来说,该文件实现了从注…...

如何实现设备远程控制?
在工业自动化领域,设备远程控制是一项非常重要的技术。它使得设备可以在远离现场的情况下进行远程操作和维护,大大提高了设备的可用性和效率。 设备远程控制的应用场景有哪些? 远程故障排除:当设备出现故障时,工程师…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...