剖析C++中的菱形继承

剖析C++中的菱形继承

文章目录

  • 剖析C++中的菱形继承
    • 前言
    • 菱形继承
    • 虚拟继承与虚基表
    • 总结

前言

在面向对象编程中,继承允许我们构建出复杂的类关系和对象模型。然而,当多个类继承自同一个基类时,可能会引发结构上的冲突和数据冗余。这种情况在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;
};

接着我们为了便于通过调试窗口观察各个类中成员的分布情况,相应的给出如下测试代码:

void test()
{A a;B b;C c;D d;a._a = 1;b._b = 2;b._a = 22;c._c = 3;c._a = 33;d._d = 4;d.B::_a = 5;d.C::_a = 6;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;cout << sizeof(d) << endl;
}

调试窗口:

在这里插入图片描述

监视内存窗口截图:
在这里插入图片描述

通过上面的内存窗口我们可以看到数据冗余,D类实例化出的对象d拥有两个分别归属于B和C的成员变量_a,为了解决这种冗余问题,我们引出了菱形虚拟继承,下面将会接着分析虚拟继承的特性:

虚拟继承与虚基表

首先我们对代码稍作修改,将A类与B、C类之间的继承关系变为虚继承:

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

测试代码基本保持不变:

void test()
{A a;B b;C c;D d;a._a = 1;b._b = 2;b._a = 22;c._c = 3;c._a = 33;d._d = 4;d._b = 44;d._c = 55;d.B::_a = 5;d.C::_a = 6;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;cout << sizeof(d) << endl;
}

监视内存窗口:

在这里插入图片描述

通过上面分析图可以得出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量,通过偏移量可以找到下面的A。

运行窗口:

在这里插入图片描述

我们看到经过虚继承的B、C类都要比之前更大,因为内部多存放了一个虚基表指针,指向用来存放类虚继承下来的成员变量的虚基表。

在虚拟继承的情况下,虚基表指针(vptr)和虚基表(vtable)的引入,确保了基类A的唯一性。这种机制允许B和C类通过虚基表指针找到共享的基类A实例。虽然这增加了一些内存开销,但它解决了数据冗余的问题,并保证了类层次中数据的一致性。


总结

​ 虚拟继承是C++中处理菱形继承问题的关键。通过将基类声明为虚基类,我们可以确保在派生类中只有一个基类实例,从而避免了数据冗余。这种方法虽然在内存布局上稍显复杂,但它提供了一种在复杂类层次中保持数据一致性的有效方式。随着对C++深入的理解,开发者可以更好地利用这些特性来设计健壮的系统。在实际应用中,虚拟继承的使用应当谨慎,以确保它不会引入不必要的复杂性。

在这里插入图片描述

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

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

相关文章

Linux 命令 top 详解

1 top命令介绍 Linux系统中&#xff0c;Top命令主要用于实时运行系统的监控&#xff0c;包括Linux内核管理的进程或者线程的资源占用情况。这个命令对所有正在运行的进程和系统负荷提供不断更新的概览信息&#xff0c;包括系统负载、CPU利用分布情况、内存使用、每个进程的内容…

Vue2电商前台项目(一):项目前的初始化及搭建

一、项目初始化 创建项目&#xff1a;sudo vue create app 1.项目配置 &#xff08;1&#xff09;浏览器自动打开 在package.json文件中&#xff0c;serve后面加上 --open "scripts": {"serve": "vue-cli-service serve --open","buil…

JimuReport 积木报表

一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于 excel 操作风格&#xff0c;通过拖拽完成报表设计…

spring总结-基于XML管理bean超详细

spring ioc总结-基于XML管理bean 前言实验一 [重要]创建bean1、目标和思路①目标②思路 2、创建Maven Module3、创建组件类4、创建spring配置文件7、无参构造器8、用IOC容器创建对象和自己建区别 实验二 [重要]获取bean1、方式一&#xff1a;根据id获取2、方式二&#xff1a;根…

【详解旋转编码器原理与应用】:从类型到作用全面解读

旋转编码器是一种精密的传感器装置&#xff0c;主要用于测量旋转物体的角度、速度、方向或位移等机械运动参数&#xff0c;并将其转换为相应的电脉冲信号或数字信号输出。这种装置广泛应用于工业自动化、机器人技术、伺服控制系统、电梯、电机控制、音视频设备、游戏控制器以及…

数据结构——二叉树(堆)

大家好我是小峰&#xff0c;今天我们开始学习二叉树。 首先我们来学习什么是树&#xff1f; 树概念及结构 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的…

前端三剑客 —— CSS (第三节)

目录 上节回顾&#xff1a; 1.CSS使用有以下几种样式; 2.选择器 1.基本选择器 2.包含选择器 3.属性选择器 [] 4.伪类选择器 &#xff1a; 5.伪元素选择器 ::before :after 3.常见样式的使用 常见样式参考表 一些特殊样式 媒体查询 自定义字体 变换效果 translate&…

从 Redis 开源协议变更到 ES 国产化:一次技术自主的机遇

引言 近日&#xff0c;Redis Labs 宣布其主导的开源项目 Redis 将采用双重源代码可用许可证&#xff08;RSALv2&#xff09;和服务器端公共许可证&#xff08;SSPLv1&#xff09;。这一重大决策标志着 Redis 从传统的 BSD 许可证向更加严格的控制权转变&#xff0c;同时也引发…

Servlet Response的常用方法 缓存和乱码处理

前言 Servlet Response相关的信息&#xff0c;在service方法中使用的是HttpServletResponse&#xff0c;它继承自ServletResponse&#xff0c;扩展了Http协议相关的内容&#xff0c;下面简单记录一下它的基本用法。 一、response组成内容 以下是一个常见response响应的内容&…

Ruby 之交租阶段信息生成

题目 我看了一下&#xff0c;这个题目应该不是什么机密&#xff0c;所以先放上来了。大概意思是根据合同信息生成交租阶段信息。 解答 要求是要使用 Ruby 生成交租阶段信息&#xff0c;由于时间比较仓促&#xff0c;变量名那些就用得随意了些。要点主要有下面这些&#xff1a…

Redission--布隆过滤器解决缓存穿透问题

布隆过滤器在缓存穿透问题中的使用 布隆过滤器的核心是一个位数组 布隆过滤器的误判 使用Redission的布隆过滤器步骤 添加 Redission 依赖&#xff1a;首先需要将 Redission 添加到你的 Java 项目中&#xff0c;你可以通过 Maven 来添加 Redission 的依赖。 创建 Redissio…

机器学习全攻略:概念、流程、分类与行业应用案例集锦

目录 1.引言 2.从零开始认识机器学习&#xff1a;基本概念与重要术语 3.五步走&#xff1a;掌握机器学习项目执行的完整流程 3.1.问题定义与数据收集 3.2.数据预处理与特征工程 3.3.模型选择与训练 3.4.模型评估与优化 3.5.模型部署与监控 4.深入了解各类机器学习方法…

LeetCode-146. LRU 缓存【设计 哈希表 链表 双向链表】

LeetCode-146. LRU 缓存【设计 哈希表 链表 双向链表】 题目描述&#xff1a;解题思路一&#xff1a;双向链表&#xff0c;函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。一张图&#xff1a;知识点__slots__ 解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&am…

使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流

使用 Flume 将 CSV 数据导入 Kafka&#xff1a;实现实时数据流 文介绍了如何使用 Apache Flume 将 CSV 格式的数据从本地文件系统导入到 Apache Kafka 中&#xff0c;以实现实时数据流处理。通过 Flume 的配置和操作步骤&#xff0c;我们可以轻松地将数据从 CSV 文件中读取并发…

医院云HIS系统源码,二级医院、专科医院his系统源码,经扩展后能够应用于医联体/医共体

基于云计算技术的B/S架构的HIS系统&#xff0c;为医疗机构提供标准化的、信息化的、可共享的医疗信息管理系统&#xff0c;实现医患事务管理和临床诊疗管理等标准医疗管理信息系统的功能。 系统利用云计算平台的技术优势&#xff0c;建立统一的云HIS、云病历、云LIS&#xff0…

设计模式12--组合模式

定义 案例一 案例二 优缺点

【ESP32S3 Sense接入语音识别+MiniMax模型+TTS模块语音播报】

【ESP32S3 Sense接入语音识别MiniMax模型TTS模块语音播报】 1. 前言2. 功能模块概述2.1 语音接入2.2 大模型接入2.3 TTS模块接入 3. 先决条件3.1 环境配置3.2 所需零件3.3 硬件连接步骤 4. 核心代码4.1 源码分享4.2 代码解析 5. 上传验证5.1 对话测试5.2 报错 6. 总结 1. 前言 …

【 书生·浦语大模型实战营】学习笔记(一):全链路开源体系介绍

&#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料&#xff0c;配有全面而有深度的专栏内容&#xff0c;包括不限于 前沿论文解读、…

我的2024java实习投递历程

每天投递一个简历吧&#xff0c;我tm投投投投投投投 3/21 周四 招商银行 招银网络科技 杭州 java实习生 4月2号笔试 笔试经验&#xff1a;45分钟 30道选择题 题目回忆版&#xff1a; 1.8进制 1-777 多少个数各位乘积为0 2.有关系R&#xff08;ABCDE&#xff09;&…

【EasyExcel】多sheet、追加列

业务-EasyExcel多sheet、追加列 背景 最近接到一个导出Excel的业务&#xff0c;需求就是多sheet&#xff0c;每个sheet导出不同结构&#xff0c;第一个sheet里面能够根据最后一列动态的追加列&#xff0c;追加多少得看运营人员传了多少需求列。原本使用的 pig4cloud 架子&…