当前位置: 首页 > news >正文

【C++历险记】面向对象|菱形继承及菱形虚拟继承

个人主页:兜里有颗棉花糖💪
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【C++之路】💌
本专栏旨在记录C++的学习路线,望对大家有所帮助🙇‍
希望我们一起努力、成长,共同进步。🍓
在这里插入图片描述

目录

  • 一、多继承以及菱形继承
  • 二、多继承引发的问题
    • 多继承二义性问题的解决方式
      • 方式一:作用域解析运算符
      • 方式二:虚拟继承
  • 三、虚拟继承解决数据冗余和二义性的原理

一、多继承以及菱形继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

比如:
在这里插入图片描述
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

比如:在这里插入图片描述

菱形继承:菱形继承是多继承的一种特殊情况,指一个派生类直接或间接地从两个或者更多个基类继承成员,而这些基类又直接或间接地继承自同一个基类。

比如:
在这里插入图片描述

下面是两种简单的菱形继承的模型:
在这里插入图片描述

二、多继承引发的问题

C++继承体系中的多继承虽然给我们提供了代码的灵活性和重用性,但是也会引发一些问题:多继承会引发菱形继承问题,而菱形继承问题又会引发菱形虚拟继承问题。

下面来看菱形继承的问题:
在这里插入图片描述
上面的对象成员模型构造中,可以看出菱形继承有数据冗余二义性的问题。在Assistant的对象中Person成员会有两份(即_age有两份)。

  • 数据冗余问题(本质就是浪费空间):存在数据重复的问题,比如Person中的_age要存储两份
  • 二义性问题访问不明确,如下图。
    在这里插入图片描述

多继承二义性问题的解决方式

C++是如何解决多继承带来的二义性问题呢?

方式一:作用域解析运算符

我们可以通过作用域解析运算符,即::来解决多继承中的二义性问题。使用作用域解析运算符来明确指定调用哪个基类的成员函数或变量。
请看:
在这里插入图片描述
在这里插入图片描述

方式二:虚拟继承

虚拟继承:用于解决菱形继承或多继承中的二义性问题的一种机制。通过使用virtual关键字,在继承链中只创建一个共同基类的实例,从而避免了二义性。

请看:

class Person
{
public:string _name; // 姓名int _age;
};
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 Test1()
{Assistant as;as.Student::_age = 18;as.Teacher::_age = 21;as._age = 24;
}

解释:

首先,有一个基类 Person,它包含了姓名 _name 和年龄 _age 两个成员变量。

接下来有两个派生类 Student 和 Teacher,它们都以虚拟继承的方式继承自基类 Person。这样做是为了避免后续的Assistant 类在同时继承Student 和 Teacher 时,包含了两个相同的 Person 实例导致的二义性问题。

最后,有一个派生类 Assistant,它同时继承自 Student 和 Teacher 类。由于 Student 和 Teacher都是以虚拟继承的方式继承自 Person,在Assistant 类中就只会有一个共同的 Person 实例。

在这里插入图片描述

调试结果如下:
在这里插入图片描述

在这里插入图片描述

三、虚拟继承解决数据冗余和二义性的原理

我们已经知道:虚拟继承是C++中的一种继承方式,用于解决多重继承中的数据冗余和二义性问题。当一个类需要从多个基类中继承,而这些基类又有共同的基类时,就会产生二义性问题。

那虚拟继承又是如何解决这些问题的呢?

现在我们来研究虚拟继承原理,下面是一个简化的菱形继承继承体系,请看:

class A
{
public:int _a;
};
class B : public A
{
public:int _b;
};
class C : public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

运行调试结果如下:

监视窗口
在这里插入图片描述

内存窗口
在这里插入图片描述

可以看到上述代码中存在数据冗余的问题类D继承了类B和类C,与此同时类B和类C都继承了类A,所以可以看到在类D中有两个继承自类A的子对象,分别来自类B和类C。因此,在类D中存在数据冗余,同一个成员变量_a在类D的内存布局中会出现两次,一次来自类B的继承,一次来自类C的继承。这是因为默认情况下,多次继承同一个基类会导致该基类的成员在派生类中有多份副本

下面来看虚拟继承是如何解决上述问题的,请看:

class A
{
public:int _a;
};
class B : virtual public A//虚拟继承
{
public:int _b;
};
class C : virtual public A//虚拟继承
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};

实例一:

int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;d._a = 0;return 0;
}

下面是上述代码虚拟继承的调试内存窗口:
在这里插入图片描述

示例二(仅仅添加了对象D d2;):

int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;d._a = 0;D d2;return 0;
}

现在再来看一下内存窗口:
在这里插入图片描述
在这里插入图片描述

示例三(再来看一个对象模型):

int main()
{B b;b._a = 1;b._b = 2;return 0;
}

在这里插入图片描述
示例四:

int main()
{D d;d._a = 1;B b;b._a = 2;b._b = 3;B* ptr = &b;ptr->_a++;ptr = &d;ptr->_a++;return 0;
}

指针 ptr 指向对象 b 或对象 d 时,无论 ptr 指向的是哪个对象,当使用 ptr->_a 访问类A的成员时,编译器都会使用存储在类D对象中的偏移量来调整指针,以便正确地访问虚基类A的成员变量 _a

好了,本文就到这里啦,再见啦友友们!!!

相关文章:

【C++历险记】面向对象|菱形继承及菱形虚拟继承

个人主页:兜里有颗棉花糖💪 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【C之路】💌 本专栏旨在记录C的学习路线,望对大家有所帮助🙇‍ 希望我们一起努力、成长&…...

【Locomotor运动模块】攀爬

文章目录 一、攀爬主体“伪身体”1、“伪身体”的设置2、“伪身体”和“真实身体”,为什么同步移动3、“伪身体”和“真实身体”,碰到墙时不同步的原因①现象②原因③解决 二、攀爬1、需要的组件:“伪身体”、Climbing、Climbable及Interacto…...

ELK安装、部署、调试(一)设计规划及准备

一、整体规划如图: 【filebeat】 需要收集日志的服务器,安装filebeat软件,用于收集日志。logstash也可以收集日志,但是占用的系统资源过大,所以使用了filebeat来收集日志。 【kafka】 接收filebeat的日志&#xff…...

【CSS】解决对齐的小问题

问题: 表单或者页面上可能遇到文字无法对平均分,带有冒号的文本无法左右对齐的情况 常见的解决方式: 解决如下图 仍无法解决对齐的问题,还需要考虑字数 解决 这里用css的方式解决 增加 i 标签 固定宽度,设置 i …...

【狂神】Spring5(Aop的实现方式)

今天没有偷懒,只是忘了Mybatis,所以去补课了~ ┏━━━━━━━━━━━━━━━┓ NICE PIGGY PIG.. ┗━━━━━━━△━━━━━━━┛ ヽ(・ω・)ノ | / UU 1.Aop实现方式一 1.1、什…...

第2章 Linux多进程开发 2.18 内存映射

内存映射:可以进行进程间的通信 1.如果对mmap的返回值(ptr)做操作(ptr), munmap是否能够成功? void * ptr mmap(…); ptr; 可以对其进行操作 munmap(ptr, len); // 错误,要保存地址 2.如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样? 错…...

【C++深入浅出】类和对象上篇(类的基础、类的模型以及this指针)

目录 一. 前言 二. 面向对象与面向过程 2.1 面向过程 2.2 面向对象 三. 类的基础知识 3.1 类的引入 3.2 类的定义 3.3 成员变量的命名规则 3.4 封装 3.5 类的访问限定符 3.6 类的作用域 3.7 类的实例化 四. 类的对象模型 4.1 类对象的大小 4.2 类对象的存储方式 …...

气象站在日常生活中的重要性

气象站在我们的日常生活中起着重要的作用,它监测着天气的变化,能够提供及时、准确的天气信息,对我们的生产和生活都有着极大的影响。 一、气象站的工作原理 气象站通过一系列传感器设备,对风速、风向、温度、湿度、气压、雨量等…...

数据结构学习系列之用队列实现栈功能与用栈实现队列功能

队列与栈:队列(Queue)是一种先进先出(FIFO)的线性表;栈(Stack)是一种后进先出(LIFO)的线性表;实例1:用队列实现栈的功能;算…...

PY32F003F18P单片机概述

PY32F003F18P单片机是普冉的一款ARM微控制器,内核是Cortex-M0。这个单片机的特色,就是价格便宜,FLASH和SRAM远远超过8位单片机,市场竞争力很强大。 一、硬件资源: 1)、FLASH为64K字节; 2)、SRAM为8K字节&…...

查看GPU占用率

如何监控NVIDIA GPU 的运行状态和使用情况_nvidia 85c_LiBiGo的博客-CSDN博客设备跟踪和管理正成为机器学习工程的中心焦点。这个任务的核心是在模型训练过程中跟踪和报告gpu的使用效率。有效的GPU监控可以帮助我们配置一些非常重要的超参数,例如批大小,…...

设计模式-中介者模式

文章目录 一、前言二、中介者模式1、定义2、未使用/使用中介者模式对比2.1、未使用中介者模式:2.2、使用中介者模式: 3、角色分析3.1、中介者(Mediator):3.2、同事(Colleague):3.3、…...

react 大杂烩

组件 1.是返回标签的js函数,是可重复利用的UI元素 function test(){ return ( test ); } 2.构建组件: (1)export 导出组件 (2)定义函数,名称必须以大写字母开头 (3&#xff09…...

图解 STP

网络环路 现在我们的生活已经离不开网络,如果我家断网,我会抱怨这什么破网络,影响到我刷抖音、打游戏;如果公司断网,那老板估计会骂娘,因为会影响到公司正常运转,直接造成经济损失。网络通信中&…...

Kubernetes技术--k8s核心技术Controller控制器

1.Controller概述 Controller是在集群上管理和运行容器的对象。是一个实际存在的对象。 2.pod和Controller之间的关系 pod通过controller实现应用的运维,包括伸缩、滚动升级等操作。 这里pod和controller通过label标签来建立关系。如下所示: 3.Deployment控制器应用场景 -1:…...

Kubernetes技术--k8s核心技术 Secret

1.概述 Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。Secret可以以 Volume 或者环境变量的方式使用。 作用 加密数据存储在/etc中,使得pod容器以挂载volume方式进行访问。在进行的数据存储中是以base64加密的方式…...

day27 String类 正则表达式

String类的getBytes方法 String s "腻害"; byte[] bytes s.getBytes(StandardCharsets.UTF_8); String类的new String方法 String ss "ss我的"; byte[] gbks ss.getBytes("gbk"); String gbk new String(gbks, "gbk"); String类的…...

Java设计模式:四、行为型模式-10:访问者模式

一、定义:访问者模式 访问者模式:核心在于同一个事物不同视角下的访问信息不同。 在一个稳定的数据结构下,例如用户信息、雇员信息等,增加易变的业务访问逻辑。为了增强扩展性,将两部分的业务解耦的一种设计模式。 二…...

【juc】读写锁ReentrantReadWriteLock

目录 一、说明二、读读不互斥2.1 代码示例2.2 截图示例 三、读写互斥3.1 代码示例3.2 截图示例 四、写写互斥4.1 代码示例4.2 截图示例 五、注意事项5.2.1 代码示例5.2.2 截图示例 一、说明 1.当读操作远远高于写操作时,使用读写锁让读读可以并发,来提高…...

Linux开机启动Tomcat

需求背景 Linux重启后要手动执行"startup.sh"启动Tomcat&#xff0c;比较麻烦&#xff0c;想要Linux开机启动Tomcat。 开机启动 #---------------------------------------------------------- sudo tee /usr/bin/tomcat.sh <<-EOF #! /bin/bash nohup /opt/to…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...