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列表框组件的常用方法及灵活运用。…

武汉灰京文化探索游戏研发中的技术关键,扩展性与接口支持的重要性

在游戏研发的旅程中&#xff0c;一旦确定了主线框架&#xff0c;接下来的产品研发阶段将成为决定游戏成败的关键时刻。然而&#xff0c;有经验的项目经理深知&#xff0c;游戏研发不仅仅是关于打磨游戏本身&#xff0c;更涉及到数据分析、灵活配置、促销、联运、运维等多个方面…

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.端口号与进…

CSS新手入门笔记整理:CSS3文本样式

文本阴影&#xff1a;text-shadow 语法 p{text-shadow:x-offset"数值" y-offset"数值" blur"数值" color"色值";} x-offset是“水平阴影”&#xff0c;表示阴影的水平偏移距离&#xff0c;单位可以是px、em和百分比等。 y-offset是…

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

超级计算机与天气预报&#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&…

Linux高级系统编程-MySQL,使用C语言操作MySQL

API 初始化 所需头文件 #include <mysql/mysql.h> 函数 : MYSQL *mysql_init(MYSQL *mysql) 描述&#xff1a; 分配或初始化与mysql_real_connect() 相适应的 MYSQL 对象 参数&#xff1a; mysql:MYSQL*句柄 返回值&#xff1a; 初始化的MYSQL* 句柄。如果无足…

uni-app基本标签

导航栏设置 - navigationBarBackgroundColor: 设置导航栏的背景颜色&#xff08;全局页面&#xff09; - navigationBarTextStyle: 导航栏标题颜色&#xff08;仅支持 black 和 white&#xff09; - navigationBarTitleText: 设置导航栏标题内容 - enablePullDownRefresh: 是否…

svn拉取

拉取前一定要记得先umont // 设置成中文编码 export LC_ALLzh_CN.UTF-8 svn checkout svn://192.168.0.207:88/svn/JavaPlatform/core/jeesite-core --username quzhonglongfuanna.com --password qzl2023 svn checkout svn://192.168.0.207:88/svn/JavaPlatform/core/jeesi…

python之彩色图像变灰度图像

目录 1、加权平均法&#xff1a; 2、最大值法&#xff1a; 3、分量法&#xff1a; 4、平均值法&#xff1a; 彩色图像转换为灰度图像的过程涉及到图像处理的基本概念和方法。这种转换是通过对彩色图像的三个通道&#xff08;红、绿、蓝&#xff0c;即RGB&#xff09;进行特…

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

前言 指针其实是我们学习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…