【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的日志ÿ…...
【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)…...
图解 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,比较麻烦,想要Linux开机启动Tomcat。 开机启动 #---------------------------------------------------------- sudo tee /usr/bin/tomcat.sh <<-EOF #! /bin/bash nohup /opt/to…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...






