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()去获取那个位置的迭代器,并不是里面有。

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

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

相关文章

Docker部署MinIO对象存储服务器结合内网穿透实现远程访问

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

OpenCV技术应用(7)— 将图像转为热力图

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教大家如何将一幅图像转化成热力图&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍 伪彩色处…

C++ Qt 开发:ListWidget列表框组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍ListWidget列表框组件的常用方法及灵活运用。…

centos下:mysql一些指令+mysql首次修改密码+mysql忘记密码修改

操作 查看mysql运行状态 systemctl status mysqld 停止mysql systemctl stop mysqld 启动mysql systemctl start mysqld 重启mysql systemctl restart mysqld 开启mysql开机自启动 systemctl enable mysqld 关闭mysql开机自启动 systemctl disable mysqld 查看具体的报错日…

AI创作系统ChatGPT网站源码,支持AI绘画,支持GPT语音对话+智能思维导图生成

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

网络编程『socket套接字 ‖ 简易UDP网络程序』

&#x1f52d;个人主页&#xff1a; 北 海 &#x1f6dc;所属专栏&#xff1a; Linux学习之旅、神奇的网络世界 &#x1f4bb;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f324;️前言&#x1f326;️正文1.预备知识1.1.IP地址1.2.端口号1.3.端口号与进…

超级计算机与天气预报:精准预测的科技革命

超级计算机与天气预报&#xff1a;精准预测的科技革命 一、引言 随着科技的飞速发展&#xff0c;超级计算机已经成为现代社会不可或缺的一部分。它们在科研、工业、军事等领域发挥着重要作用&#xff0c;其中天气预报是一个颇具代表性的应用领域。本文将探讨超级计算机在天气…

[ CTF ]【天格】战队WriteUp-第七届“强网杯”全国安全挑战赛

第七届“强网杯”全国安全挑战赛 2023.12.16~2023.12.17 文章目录 【Misc】Pyjail ! Its myFILTER !!!easyfuzz谍影重重2.0签到Pyjail ! Its myRevenge !!!server_8F6C72124774022B.py 问卷调查 【Reverse】ezre 【Web】happygame 【强网先锋】石头剪刀布TrieSpeedUpezreez_fmt…

最新AI创作系统ChatGPT系统源码+DALL-E3文生图+支持AI绘画+GPT语音对话功能

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

[linux]进程间通信-管道pipe的实际用法(写入/读取)

一、需求 现有两个进程A和B&#xff0c;B进程含较为独立且复杂的业务逻辑&#xff0c;A进程为主控进程&#xff0c;现A进程需要控制B进程执行对应的功能&#xff0c;且要保持响应及时。 二、分析 典型进程间通信案例&#xff0c;因此使用linux下的管道方法&#xff08;pipe&…

打响指针的第一枪:指针家族

前言 指针其实是我们学习C语言中最难的知识点&#xff0c;很多人在学习指针的时候会被绕晕&#xff0c;包括博主也是&#xff0c;当初百思不得其解&#xff0c;脑袋都要冒烟了&#xff0c;本来打算在学习指针的时候就写一篇博客&#xff0c;但是当初自己的能力还是没有办法去完…

SQL语句整理二--Mysql

文章目录 知识点梳理&#xff1a;1. mysql 中 in 和 exists 区别2. varchar 与 char 的区别 查看表结构&#xff1a;获取当前时间&#xff1a;查看建表语句&#xff1a;修改用户密码&#xff1a;查看所有用户&#xff1a;grant命令&#xff1a;判断当前数据库有多少连接数&…

WPF Icon矢量库 MahApps.Metro.IconPacks

文章目录 前言MahApps.Metro.IconPacksIconPacks.Browser简单使用简单使用案例代码Icon版本个人推荐 Icon自定义版权问题 前言 为了更快的进行开发&#xff0c;我找到了一个WPF的矢量图库。这样我们就不用去网上找别人的矢量库了 MahApps.Metro.IconPacks MahApps.Metro.Icon…

21.Servlet 技术

JavaWeb应用的概念 在Sun的Java Servlet规范中&#xff0c;对Java Web应用作了这样定义&#xff1a;“Java Web应用由一组Servlet、HTML页、类、以及其它可以被绑定的资源构成。它可以在各种供应商提供的实现Servlet规范的 Servlet容器 中运行。” Java Web应用中可以包含如下…

BearPi Std 板从入门到放弃 - 先天神魂篇(9)(RT-Thread DAC->ADC)

简介 RT-Thread DAC->ADC 使用, 就是DAC1输出模拟量, ADC1 读取模拟量转化成电压值, 基于开发板 &#xff1a; Bearpi Std(小熊派标准板)主芯片: STM32L431RCT6串口: Usart1DAC1: PA5, OUT2ADC1: PC2, IN3将板子上的E53 接口, 5 和 6用排线相连, 即实现内部DAC1->ADC1 …

P2P网络下分布式文件共享场景的测试

P2P网络介绍 P2P是Peer-to-Peer的缩写&#xff0c;“Peer”在英语里有“对等者、伙伴、对端”的意义。因此&#xff0c;从字面意思来看&#xff0c;P2P可以理解为对等网络。国内一些媒体将P2P翻译成“点对点”或者“端对端”&#xff0c;学术界则统一称为对等网络(Peer-to-Pee…

深入浅出RPC:选取适合自己的RPC

文章目录 1、RPC概念&&背景1.1、RPC背景 1.2、RPC是什么&#xff0c;什么时候需要用到&#xff1f;2、进程间的通信 - IPC与RPC2.1、什么是IPC2.2、IPC与RPC联系 3、RPC的实现3.1、RPC实现的基本思路3.2、RPC实现的扩展方向 4、RPC的选择 1、RPC概念&&背景 1.…

【 AI 两步实现文本 转 语音】

基于hugging face 中 XTTS-v2 模型做文本转语音&#xff0c;此模型支持17种语言 1.登录hugging face 官网 https://huggingface.curated.co/ 或者 https://hf-mirror.com/models 找到models处下载XTTS-V2 如果你全程可以联网&#xff08;/huggingface.co&#xff09;直接步骤…

SpringBoot中使用@Async实现异步调用

SpringBoot中使用Async实现异步调用 什么是异步调用?异步调用对应的是同步调用&#xff0c;同步调用指程序按照定义顺序依次执行&#xff0c;每一行程序都必须等待上 一行程序执行完成之后才能执行&#xff1b;异步调用指程序在顺序执行时&#xff0c;不等待异步调用的语句返…

32位MCU极致性价比高速风筒方案特点--【其利天下技术】

近年来&#xff0c;伴随着人们消费升级及现代工业技术水平的提升&#xff0c;电吹风市场已经步入了绿色节能、高效多功能化的发展阶段。人们对电吹风的需求和要求都在不断增加。然而&#xff0c;传统电吹风采用交流电机&#xff0c;使用寿命有限&#xff0c;维护不方便&#xf…