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

【C++】复杂的多继承及其缺陷(菱形继承)

本篇要分享的内容是C++中多继承的缺陷:菱形继承。

以下为本篇目录

目录

1.多继承的缺陷与解决方法

2.虚继承的底层原理

3.虚继承底层原理的设计原因


1.多继承的缺陷与解决方法

首先观察下面的图片判断它是否为多继承

这实际上是一个单继承,单继承的特点是一个子类只有一个直接继承的父类,即使又多层继承关系,但是只有一个直接父类,都称作单继承 

多继承的图示如下

 可以看到多继承中的子类扮演了两个角色,就相当于桃花既能开出好看的桃花,也能结果。

所以多继承的特点是一个子类有两个或以上的直接父类时称这个关系叫做多继承。

那在上图中使用多继承是没有错误的,他可以在一个类中结合多个类的特点,多继承的本身并没有错误,但是出错的往往是在一些使用场景下会有缺陷,如下图

有了多继承可能就会导致菱形继承(如上图)。

可以看到Student类和Teacher类都会继承Person中的属性,

但是此时Assistant同时又继承了Student类和Teacher类的话,Person中的属性在Assistant中就会出现两次,会有二义性。

这也是为什么java语言中没多继承用法的原因。

观察如下代码

#include<iostream>
using namespace std;
class Person
{
public:string _name; // 姓名
};class Student :  public Person
{
protected:int _num; //学号
};class Teacher : 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";
}

 可以看到在报错中它有规定使用的访问限定符,也就是说它会将同样的属性及信息再继承一份,也就是同时具有两份数据信息从而导致数据冗余占用空间,

如果Person类的空间很大,那么浪费的空间会更大。

那如何解决这样的问题呢?

首先我们可以使用访问限定符解决二义性

,这样使用没有问题

其次是在出现菱形继承的玩儿法之后C++祖师爷又更新了一个关键字:virtual(虚拟)

我们只需要在被多继承的类的继承方法前加上virtual,即可使用虚继承,

#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";
}

使用了虚继承之后就可以调用冗余数据的属性了

 

 可以看到,使用了虚继承之后不管是Student类中的_name还是Person类中的_name都不管用了,使用a直接可以调用_name,并且所用的空间也是同一份地址空间。

这样就解决了在菱形继承中数据冗余的问题。

但是在一个庞大的项目中这样的问题语法依旧会坑害不少人,所以尽量的能少用多继承,就要少用多继承。

2.虚继承的底层原理

既然要了解菱形继承的底层原理,我们不妨设计一个简单一点的代码便于观察,代码如下

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;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

那上面的菱形继承关系也很简单,如下图

除了观察菱形继承外,main函数中的内容对菱形继承的测试也同样重要;

我们将代码调试,并观察内存窗口。

使用D创建了对象d,在内存中观察d的地址即可。

 可以看到的是在B类存放了两个两个值,1和3

C类中也存放了两个值,2和4

D类中存放了一个值2,他与对象d中修改_d的值相同;

那这样存放数据是什么意思呢?

上面的代码没有使用虚函数,所以存放了两个值,导致了数据的二义性;

接下来我们使用虚函数,虚函数可以解决数据冗余和二义性的问题,我们继续观察内存的变化

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;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

使用了虚函数继续观察内存模块

和上面对比我们发现,在B类中存放了两行数据:第一行为一个地址,第二行为所修改的数据;

在C类中,存放的数据与B类相似

D和A中的数据被修改为最后所修改的数据;

可以看到在B类和C类中将地址取代了第一次所存的数据,从而达到解决数据二义性的目的;

那这个存放的地址又是什么意思是呢?

 我们继续再调出一个监视内存的窗口来观察

再第二个观察内存的窗口中输入B类的地址,这时你就会发现在第二个内存表中存在一个数,这个数子就是距离最终修改a的偏移量,上图为十六进制的14

当我们将B类中的第一行存放的地址加上十六进制的14,就会得到_a最终的值,_a=0;

我们再来举出一组例子来证明不是巧合

 可以看到_a只被赋值,而B中不仅存放了_b的值,同样也存放了一个指针,指向了距离A的偏移量,也同样是将B类中第一行的地址加上指针所指的偏移量(8),就是1所在的位置。

以上就是设计的原理,虽然设计很多内存和地址的关系,但是这就是虚函数底层的实现设计。

3.虚继承底层原理的设计原因

那为什么要这么设计呢?

如以下情况

一个B类创建的指针会指向bb对象,也有可能指向d对象,

所以我们无法得知这个指针所指向的对象,就只能靠指针来检查另一块内存上所存放的偏移量,通过计算偏移量来计算虚继承中二义性的变量。

以上就是菱形继承的设计缺陷以及后序的设计的解决思路,以及解决思路的底层设计。

其实多继承本身没有问题,只是菱形继承的用法让多继承成为了大坑。

即使本人水平有限,尽管不遗余力但本篇对虚继承的探索仍有不足,还请读者指正,感谢您的阅读。

相关文章:

【C++】复杂的多继承及其缺陷(菱形继承)

本篇要分享的内容是C中多继承的缺陷&#xff1a;菱形继承。 以下为本篇目录 目录 1.多继承的缺陷与解决方法 2.虚继承的底层原理 3.虚继承底层原理的设计原因 1.多继承的缺陷与解决方法 首先观察下面的图片判断它是否为多继承 这实际上是一个单继承&#xff0c;单继承的特…...

esp32-rust-no_std-examples-blinky

什么是裸机环境&#xff1f; 裸机环境是指没有可供使用的操作系统环境。当编译的 Rust 程序拥有 no_std 属性时&#xff0c;该程序无权访问上述 std 章节中提到的某些特定功能。尽管仍支持使用配网或引入复杂数据结构等功能&#xff0c;但实现方式将会更加复杂。 no_std…...

GitHub上的开源工业软件

github上看到一个中国人做的流体力学开源介绍&#xff0c;太牛了&#xff01; https://github.com/clatterrr/FluidSimulationTutorialsUnity 先分析一下工业仿真软件赛道 工业仿真软件的赛道和产品主要功能如下&#xff1a; 1. 工艺仿真赛道&#xff1a; - 工厂布局优化&am…...

Centos7安装配置中文输入法

Centos7安装配置中文输入法 在安装CentOS时&#xff0c;我们为了方便使用&#xff0c;语言选择了中文&#xff0c;但是我们发现&#xff0c;在Linux命令行或者是浏览器中输入时&#xff0c;我们只能输入英文&#xff0c;无法输入汉字。 来&#xff0c;跟随脚步&#xff0c;设…...

【OJ比赛日历】快周末了,不来一场比赛吗? #11.11-11.17 #12场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-11-11&#xff08;周六&#xff09; #5场比赛2023-11-12…...

提取当前文件夹下多文件夹中的数据

提取当前文件夹下多文件夹中的数据 1.实现步骤 现在D:\临时\图库 这个文件夹下有多个文件夹,现在需要将多个文件夹中的文件全部移动到D:\临时\图库下; $sourcePath "D:\临时\图库" $targetPath "D:\临时\图库"Get-ChildItem -Path $sourcePath -File …...

深度学习(生成式模型)——Classifier Free Guidance Diffusion

文章目录 前言推导流程训练流程测试流程 前言 在上一节中&#xff0c;我们总结了Classifier Guidance Diffusion&#xff0c;其有两个弊端&#xff0c;一是需要额外训练一个分类头&#xff0c;引入了额外的训练开销。二是要噪声图像通常难以分类&#xff0c;分类头通常难以学习…...

C语言 每日一题 11.9 day15

数组元素循环右移问题 一个数组A中存有N&#xff08; > 0&#xff09;个整数&#xff0c;在不允许使用另外数组的前提下&#xff0c;将每个整数循环向右移M&#xff08;≥0&#xff09;个位置&#xff0c;即将A中的数据由&#xff08;A0​A1⋯AN−1&#xff09;变换为&…...

STM32F103C8T6第三天:pwm、sg90、超声波、距离感应按键开盖震动开盖蜂鸣器

1. 定时器介绍1&#xff08;317.21&#xff09; 软件定时&#xff08;之前的定时方法&#xff09;&#xff08;软件延时&#xff09;缺点&#xff1a;不精确、占用CPU资源 void Delay500ms() //11.0592MHz {unsigned char i, j, k;_nop_();i 4;j 129;k 119;do{do{while (-…...

栈的顺序存储实现(C语言)(数据结构与算法)

栈的顺序存储实现通常使用数组来完成。实现方法包括定义一个固定大小的数组&#xff0c;以及一个指向栈顶的指针。当元素入栈时&#xff0c;指针加一并将元素存储在相应位置&#xff1b;当元素出栈时&#xff0c;指针减一并返回相应位置的元素。 1. 顺序栈定义 #define MaxSi…...

设计模式 -- 观察者模式

说明 author blog.jellyfishmix.com / JellyfishMIX - githubLICENSE GPL-2.0 定义 观察者模式(Observer Design Pattern) 也被称为发布订阅模式(Publish-Subscribe Design Pattern)。在 GoF 的《设计模式》一书中&#xff0c;它的定义是这样的: Define a one-to-many depe…...

Go RabbitMQ简介 使用

RabbitMQ简介 RabbitMQ 是一个广泛使用的开源消息队列系统&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;标准&#xff0c;为分布式应用程序提供了强大的消息传递功能。RabbitMQ 是 Erlang 语言编写的&#xff0c;具有高度的可扩展性和可靠性&#xff0c;…...

【面经】Spring框架中用了哪些设计模式

在Spring框架中&#xff0c;主要运用了以下几种设计模式&#xff1a; 工厂模式&#xff1a; Spring beanFactory使用工厂模式在应用程序中管理对象的创建。 通过使用工厂模式&#xff0c;Spring可以将对象的创建与使用分离&#xff0c;降低耦合度。 单例模式&#xff1a; Spr…...

SpringBoot自动配置的原理篇,剖析自动配置原理;实现自定义启动类!附有代码及截图详细讲解

SpringBoot 自动配置 Condition Condition 是在Spring 4.0 增加的条件判断功能&#xff0c;通过这个可以功能可以实现选择性的创建 Bean 操作 思考&#xff1a;SpringBoot是如何知道要创建哪个Bean的&#xff1f;比如SpringBoot是如何知道要创建RedisTemplate的&#xff1f;…...

苹果Ios系统app应用程序开发者如何获取IPA文件签名证书时需要注意什么?

今天呢想和大家介绍介绍苹果App开发者如何获取IPA文件签名证书的步骤和注意事项。对于苹果应用程序开发者而言&#xff0c;获取IPA文件签名证书是发布应用程序至App Store的重要步骤之一。签名证书能够确保应用程序的安全性和可信度&#xff0c;并使其能够在设备上正确运行。 …...

算法通关村第七关-黄金挑战二叉树迭代遍历

大家好我是苏麟 , 今天带来二叉树的迭代遍历 . 二叉树的迭代遍历 前序编列 描述 : 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 题目 : LeetCode 二叉树的前序遍历 : 144. 二叉树的前序遍历 分析 : 前序遍历是中左右&#xff0c;如果还有左子树就一…...

2023-11-Rust

学习方案&#xff1a;Rust程序设计指南 1、变量和可变性 声明变量&#xff1a;let 变量、const 常量 rust 默认变量一旦声明&#xff0c;就不可变(immutable)。当想改变 加 mut&#xff08;mutable&#xff09; 。 const 不允许用mut &#xff0c;只能声明常量&#xff0c;…...

iOS代码混淆----自动

先大致解释一下“编译"、"反编译": 编译&#xff1a;就是把千千万万行字符串(也叫代码&#xff0c;或者源文件)&#xff0c;变成010101010101(机器码&#xff0c;也叫目标代码) 编译过程&#xff1a;预处理-编译-汇编-链接 我的脚本运行在预处理阶段。 反编…...

对Mysql和应用微服务做TPS压力测试

1.对Mysql 使用工具&#xff1a;mysqlslap工具 使用命令&#xff1a; mysqlslap -uroot pGG8697000!#--auto generate sql -auto generate sql-load typemixed-concurrency100,200 - number of queries1000-iterations10 - number-int-cols7 - number-charcols13auto genera…...

将程序添加至右键菜单

将程序添加至右键菜单 手动导入 如果要将cmder添加至右键菜单。可以通过编写reg注册表方式添加 也可以在路径HKEY_CLASSES_ROOT\Directory\Background\shell中右击添加 创建项commadn 编写reg注册表 [HKEY_CLASSES_ROOT\Directory\Background\shell\cmder]为注册表地址 Wi…...

MedGemma-X在教学查房中的应用:AI实时生成鉴别诊断思维导图

MedGemma-X在教学查房中的应用&#xff1a;AI实时生成鉴别诊断思维导图 1. 教学查房的痛点与AI解决方案 教学查房是医学教育中至关重要的环节&#xff0c;但传统方式存在几个明显痛点&#xff1a; 时间压力大&#xff1a;在有限的时间内&#xff0c;带教老师需要完成患者诊疗…...

文泉驿微米黑字体:如何在5MB内实现完美多语言显示

文泉驿微米黑字体&#xff1a;如何在5MB内实现完美多语言显示 【免费下载链接】fonts-wqy-microhei Debian package for WenQuanYi Micro Hei (mirror of https://anonscm.debian.org/git/pkg-fonts/fonts-wqy-microhei.git) 项目地址: https://gitcode.com/gh_mirrors/fo/fo…...

别再只会用kafka-topics.sh了!这5个Kafka命令行实战场景,运维和开发都得会

别再只会用kafka-topics.sh了&#xff01;这5个Kafka命令行实战场景&#xff0c;运维和开发都得会 Kafka作为现代数据管道的核心组件&#xff0c;其命令行工具远不止于基础的topic管理。真正的高手往往能在故障排查、性能调优等关键时刻&#xff0c;通过命令行组合拳快速定位问…...

颜色科学避坑指南:CIE Lab转sRGB时,你的D65白点参数设置对了吗?

颜色科学避坑指南&#xff1a;CIE Lab转sRGB时&#xff0c;你的D65白点参数设置对了吗&#xff1f; 在数字图像处理领域&#xff0c;颜色空间的转换看似简单&#xff0c;实则暗藏玄机。许多开发者和设计师都曾遇到过这样的困惑&#xff1a;明明按照标准公式实现了从CIE Lab到sR…...

扩展拖垮VSCode?禁用这3类高危插件,启动速度提升3.2倍,实测有效

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;扩展拖垮VSCode&#xff1f;禁用这3类高危插件&#xff0c;启动速度提升3.2倍&#xff0c;实测有效 VSCode 启动缓慢常被误认为是硬件或系统问题&#xff0c;但真实瓶颈往往藏在插件生态中。我们对 127…...

互联网大厂Java求职面试问答详解:核心技术栈与业务场景实战

互联网大厂Java求职面试问答详解&#xff1a;核心技术栈与业务场景实战 面试背景 本次面试模拟场景为互联网大厂Java职位&#xff0c;面试官严肃专业&#xff0c;面试者谢飞机为一个水货程序员。问答涵盖Java SE、Spring Boot、微服务、消息队列、安全框架等多个技术栈&#xf…...

【超详细】2026年Hermes Agent/OpenClaw腾讯云喂饭级10分钟部署步骤

【超详细】2026年Hermes Agent/OpenClaw腾讯云喂饭级10分钟部署步骤。OpenClaw&#xff08;前身为Clawdbot/Moltbot&#xff09;作为开源、本地优先的AI助理框架&#xff0c;凭借724小时在线响应、多任务自动化执行、跨平台协同等核心能力&#xff0c;成为个人办公与轻量团队协…...

2025届毕业生推荐的六大AI辅助写作助手解析与推荐

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 作为一种新兴辅助工具的AI写论文&#xff0c;现正逐渐去改变传统写作模式&#xff0c;&…...

远程开发环境突然断连?资深SRE披露VSCode Remote Extension 1.92+版本3个未公开的session保活机制

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;远程开发环境断连现象的典型特征与影响面分析 远程开发环境&#xff08;如 VS Code Remote-SSH、GitHub Codespaces、JetBrains Gateway&#xff09;在企业级协作中日益普及&#xff0c;但其稳定性高度…...

四博 AI 智能音箱 4G S3 版本技术方案

下面这版更偏技术方案 原型开发说明 可落地代码骨架&#xff0c;适合放到方案书、技术推广文档、客户交流材料中。代码以 ESP-IDF / ESP32-S3 风格写&#xff0c;重点突出四博方案的工程结构、联网切换、远场拾音、实时打断、MCP 扩展、屏幕异显和客户系统接入。四博 AI 智能…...