菱形继承和菱形虚拟继承(原理:虚基表)

C++多态(五)---菱形继承中的虚函数表详解 C++多态 菱形继承 虚基表指针与虚基表 我们知道,在虚继承(菱形继承)中,对于类D创建的对象d,为了避免A类中的变量重复,所以,将A类中成员变量放在d对象模型的最下面,由B类与C类各自虚基表指针访问虚基表中偏移量的方式进行访问A类成员变量。 虚函数表指针与虚函数表(虚表) 在类中成员函数实现多态时,需要将成员函数定义为虚函数,所以,在多态的情景下,单继承下会存在一个虚函数表指针,指向成员函数... 阅读详情

在继承体系中有单继承、多继承、和菱形继承,(菱形继承其实是多继承的一种特殊情况)。

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类有两个及以上个直接父类称这个继承关系为多继承
在这里插入图片描述
菱形继承:多继承的一种特殊情况。一个子类有多个直接父类并且这些直接父类的父类是同一个父类
在这里插入图片描述

菱形继承带来的问题:

拿上面菱形继承的图中的例子来看:因为有子类 Assistant 是继承自多个父类的 (Student 和 Teacher),那么多个父类又继承自同一个父类 Person,因此会导致来自总父类这样就引发了两个问题:
① 数据冗余:来自 Person 的成员变量有多份(在Assistant中有来自Student和Teacher分别继承自Person的成员变量,这两份是相同的)
② 数据二义性:在Assistant中对这些冗余的数据不能直接进行赋值,因为不知道是在给谁赋值

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中有两份来自Person继承给子类的成员_name;
	//这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "Mark";
}

可以看到对 _name 访问不明确导致程序出错。
在这里插入图片描述
但是我们可以通过类名访问到不同的类中的成员分别进行赋值。这样就可以具体到给某一个类中成员赋值。比如上面的程序中 Test 可以修改如下:

void Test()
{
	Assistant a;
	a.Student::_name = "Mark";
	a.Teacher::_name = "lzl";
}

在这里插入图片描述
可以看到已经将来自不同的成员分别进行了赋值。这样就解决的数据二义性的问题。但是,这样并没有解决数据冗余的问题。那么接下来就是祭出菱形虚拟继承的时候了。


菱形虚拟继承是什么呢?
我们来看看下面没有虚拟继承的代码:

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

来看看对象模型中存的是什么:
在这里插入图片描述
再看看下面有虚拟继承的代码以及对象模型中存储着什么:

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

对象模型:
在这里插入图片描述
可以看出通过菱形虚拟继承的对象模型中多出了几行不知道是什么的数据!!!
其实那就是一串地址:指向了虚基表的位置。
那么虚基表中存的是什么呢?
虚基表中存放的是一个偏移量,一个当前位置相对于公共父类成员的偏移量。
这样就可以通过偏移量来找到公共父类成员的位置并对其进行操作。

为什么菱形虚拟继承后的对象模型有 24 个字节,而菱形继承的对象模型有 20 个字节,看起来菱形虚拟继承的数据成员还比虚拟继承的数据成员多。
  其实这是因为我们这里用的数据量比较小的原因。当继承下来的数据变多时,那么差距就显示出来了,虚拟继承中的 B 和 D 中都会有一份继承自公共父类的数据成员,这样就有了两份相同的数据。
  而菱形虚拟继承中只是在 B 和 D 中多出了四个字节的虚基表的地址。继承自公共父类的数据成员只有一份。这样就解决了数据冗余的问题了。

那么为什么需要让 B 和 D 找到属于自己的继承自公共父类的成员呢?
原因就是为了在赋值的时候能够执行一个完整的切片的操作:看下面的代码:

D d;
B b = d;
C c = d;

就是为了让最终的子类给父类赋值时,要能够找出 B 或 C 成员中继承自 A 的成员,这样才能正确赋值。


is-a 和 has-a 的区别:

public继承就是一种 is-a 的关系,因为子类继承了父类,也就是说每个子类对象的都是父类对象。类继承允许根据其他类来定义一个类,也就是说通过父类来生成一个子类。这种生成子类的复用通常被称为 “ 白盒复用 ” 。“白盒” 是针对于可视性而言的,父类内部的细节实现在子类中是可见的。

组合就是一种 has-a 的关系,在B对象中组合另一个A对象,那么每个B对象都有了一个A对象。这要求A对象具有良好定义的接口。这样就是一个 “ 黑盒复用 ”,在B中并不能看到A的细节实现,只用到了A的接口。

那么继承和组合到底用哪个好呢?
其实主要还是根据需求来判断使用哪个。
继承的缺点:可以看做是破坏了封装,因为在子类中暴露的父类的实现细节。这样本来就违反了面向对象的三大特性之一。而且基类和派生类之间还有很强的依赖关系,耦合度高,这样也违反了 “ 高内聚,低耦合 ” 的特性。
而组合相比于继承来说并没有破坏封装,并且对象之间也没有很强的依赖性。这样针对于用户也比较友好。

总而言之,能使用组合就使用组合,尽量不要使用继承,除非你知道自己在做什么!

继承(六)—— 菱形继承的问题以及解决方案(初识虚基表) 了解菱形继承的问题以及解决方案 阅读详情

相关推荐

C++之菱形继承与虚继承(含虚函数)

面向对象的三大特征:封装,多态,继承前面我们已经讲了继承的一些知识点,在这基础上,我们讲的时候再涉猎一些多态的只是。下面我们先接着上次讲有虚函数的菱形虚继承首先什么是虚函数。?虚函数:在类里面,函数前面有virtual关键字的成员函数就是虚函数。代码块:class base { public: base() { cout << "base()" << endl; ...

Koma_CC的博客 1620

虚基类解决菱形继承

C++学习笔记

guanxiaozhi的博客 568

零基础小白Coze知识普及【新手必备资料!!】

AI智能体|扣子(Coze)工作流搭建【零基础小白Coze知识普及】 1、领取100+工作流源码!复制到扣子即可直接使用 2、AI认知+9节商业智能体拆解 3、500+各行业高质量提示词 4、超低价格使用即梦文生图图生视频和banana生图

Zhima0522的博客 3250

c++中的虚基表

看出,虽然B和C都没有存储A,但是却存储里一个地址,这个就是虚基表的地址,通过内存可以看见虚基表里面存储着两个值,第一个就是A的地址,下一行就是B距离A的偏移量,这样就可以通过虚基表找到A的存在,可以看出C也是如此。由于是虚继承,所以这里用somebody构造对象时,age是一个唯一的值,只存于对象person那部分。而如果teacher和student里面没有存着age,我们又可以通过对象来修改age,这是什么原因。虚基表的产生是由于实现虚继承,而虚继承是为了实现数据的唯一性。

weixin_74994990的博客 353

2023东华大学OJ机试题22-70

东华大学计算机考研复试机试22 约瑟夫环223、整除的尾数24、回文质数25、汽水瓶26、阶乘最后的非0位27、算菜价28、 水果价格29、求奇数的乘积30、求最晚和最早日期31、素数32、计算e33 数字之和34 繁殖问题35 奇妙的数字36 整除的尾数37 黑色星期五38 树39 约瑟夫环40 最大与最小41 环42 求数列项43 最高频率44 三艘船45 回文数46 特殊四位数47 最大值48 数列149 修理牛棚50 按要求输出序列51 部落人乘法52 序列53 双重回文数54 等差数列55 人见人

m0_49263811的博客 2598

ESXI 8.0.3 ubuntu 24.04 直通显卡P40

手把手,一步步教你直通GPU,ESXI 8.0.3 ubuntu 24.04 直通显卡P40

qq_36433118的博客 4682

C++继承与虚拟继承终极指南

在C++中,继承是面向对象编程的核心机制,它允许类(派生类)从另一个类(基类)获取成员变量和成员函数。继承实现了代码重用和多态性,但如果不当使用,可能会导致问题如菱形继承。下面我将逐步解析继承方式(包括公有、保护和私有继承)以及菱形虚拟继承的机制、问题和解决方案。通过以上解析,您应该对C++继承方式和菱形虚拟继承有了深入理解。在实际开发中,合理选择继承方式并使用虚拟继承解决特定问题,能提升代码的健壮性和可维护性。为了解决菱形继承问题,C++引入了虚拟继承(virtual inheritance)。

q_438141的博客 331

PPO实战:如何用强化学习让小车爬上MountainCarContinuous-v0山顶(附完整代码)

本文提供了使用PPO算法解决MountainCarContinuous-v0环境的完整实战教程。针对该环境的连续动作空间和稀疏奖励挑战,文章详细解析了PPO智能体的网络架构、状态归一化、GAE优势估计等核心实现,并附带了可直接运行的PyTorch代码。同时分享了关键的调参技巧和实战避坑指南,帮助读者高效训练智能体完成小车爬坡任务。

linux的博客 968

C++ 继承和多态 设计模式

/ 学号int _major;// 专业class 派生类 : 继承方式 基类 {// ... };Student:派生类public:继承方式Person:基类概念作用域条件重载同一作用域函数名相同,参数不同重写/覆盖基类和派生类作用域函数名、参数、返回值相同(协变例外),且必须是虚函数重定义/隐藏基类和派生类作用域函数名相同,不构成重写就是隐藏。

ztztz111的博客 197

计算机视觉论文复现指南:从理论到可运行代码的保姆级教程

计算机视觉论文复现指南:从理论到可运行代码的保姆级教程​ ,人工智能,计算机视觉,大模型,AI,本文围绕计算机视觉论文复现展开,介绍从理论到可运行代码的全流程。先强调复现的重要性,再讲解复现前的准备,包括明确目标、构建工具链;接着说明代码获取分析、环境配置调优、调试运行技巧及结果验证优化方法,还推荐了高效工具,提及复现中的伦理规范。旨在助读者掌握复现方法论,实现从经典模型到前沿论文的复现,推动学术成果落地。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 12万+

仿真学习系列(三十八):Sigrity仿真之单端S参数提取

S 参数(Scattering Parameters)是高速信号分析中最基础也最常用的指标,能够全面表征走线、过孔、连接器等互连结构的高频行为。在 Sigrity PowerSI 中,Model Extraction 是最常用的 S 参数提取方式,适用于高速差分线、单端线、过孔、参考平面等各种拓扑结构。 本节将通过示例讲解如何在 PowerSI 中完成一次完整的 S 参数提取流程,包括:对象选择、端口设置、频率范围配置以及结果查看。掌握此方法后,即可对任意高速网络执行阻抗、插损、回损、串扰分析,为进一步的

案例文章,高速信号 838

C++大坑之——多继承(菱形继承)

详解C++大坑——菱形继承🥳🥳🥳

Jdxxwu的博客 6026

【无人机项目实战】第三章:fast_planner工程解读(附C++代码)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、规划系统运行逻辑【业务部分】1.Fast_planner_node.cpp【程序入口】功能代码实现2.kino_replan_fsm.cpp【状态机转移】功能代码实现KinoReplanFSM::init()函数订阅目标点回调函数订阅里程计回调函数检查路径规划是否成功【重要】执行状态机FSM定时器回调函数【重要】判断目标点是否有障碍物或者是轨迹执行过程中是否有障碍物的回调函数3

qq_35635374的博客 1万+

CD59.【C++ Dev】继承(3) 菱形继承、菱形虚拟继承和虚基表的反汇编分析

探讨了C++中的继承方式,重点分析了菱形继承带来的数据冗余和二义性问题。通过代码示例展示了普通多继承的缺陷,并介绍了虚拟继承的解决方案。详细解析了虚基表(vbtable)的工作原理,包括偏移量存储方式和内存布局。通过反汇编代码分析,验证了虚继承如何通过偏移量访问公共基类成员,既解决了二义性问题又避免了数据冗余。最后比较了虚拟继承与传统继承在内存占用和访问效率上的差异,指出虚继承通过存储偏移量的方式实现了更高效的继承结构。

zhangcoder的博客 记录学习轨迹~ 992

看懂 SAP S/4HANA 产品主数据里的 GTIN:一串号码背后的全球商品识别与业务协同

SAP S/4HANA中的GTIN管理是企业供应链运营的关键环节。本文深入解析GTIN的业务内涵及其在SAP系统中的实现方式:1) GTIN是全球通用的商品标识码,不同于企业内部物料编码;2) SAP主数据中GTIN与计量单位强绑定,需为不同包装层级维护独立GTIN;3) 同一产品可能因渠道、供应商差异存在多个GTIN;4) GTIN贯穿零售POS、仓储管理、EDI订单等核心业务流程;5) 通过典型案例说明错误维护GTIN将导致收银异常、仓库作业低效等运营问题。文章强调GTIN管理需结合包装层级、计量单位等

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1432

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

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

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

m0_74352571的博客 2056

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【C++】复杂的菱形继承 及 菱形虚拟继承的底层原理

详解C++的菱形继承 及 虚拟继承如何解决菱形继承的数据冗余和二义性问题,继承与组合的对比

m0_70980326的博客 1396

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

从内存偏移量看虚基表:手把手教你破解C++菱形继承

本文深入剖析了C++菱形继承问题的核心解决方案——虚基表机制。通过分析内存偏移量和实际调试案例,手把手教你理解虚拟继承如何通过虚基表指针间接寻址,解决数据冗余和访问二义性,并揭示其背后的内存布局与性能考量。

tea88的博客 871

虚拟继承:破解菱形继承之谜

本文探讨了C++中虚拟继承机制及其在解决菱形继承问题中的应用。主要内容包括:1. 虚拟继承的引入背景,解决传统菱形继承导致的数据冗余和访问二义性问题;2. 虚拟继承的正确实现方式,强调virtual关键字应加在中间层类继承基类时;3. 详细分析虚拟继承的内存布局,包括虚基表指针和偏移量计算机制;4. 讨论构造函数调用顺序、指针偏移等进阶话题,以及标准库中的实际应用案例。文章指出,虽然虚拟继承有效解决了菱形继承问题,但也增加了实现复杂度,建议在实际开发中优先考虑组合而非继承来避免此类问题。

祯的博客 1117

C++:菱形继承问题

在C++中,菱形继承是指一个派生类同时继承自两个直接或间接基类,而这两个基类又继承自同一个基类。这种继承结构形成了一个菱形的图形,因此得名菱形继承。菱形继承可能会引起一些问题,其中最主要的问题是数据冗余。由于派生类继承了两个基类,这两个基类都可能包含相同的数据成员,导致派生类中存在两份相同的数据,造成数据冗余和浪费内存。单继承:一个子类只有一个直接父类时称这个继承关系为单继承多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承菱形继承:菱形继承是多继承的一种特殊情况。

decade777555的博客 2596

C++_继承(菱形继承与虚基表)

什么是继承 继承是什么: 继承是面向对象程序设计、使得代码可以复用的重要手段,它允许程序员在保持原有特性的基础上进行扩展,增加功能,这样产生的新类,称之为派生类 继承的目的: 让子类继承和复用父类定义的成员和方法、继承下来的变量是独立的、各自拥有各自的内存空间。 继承方式和访问限定符 可见在派生类中,子类的访问方式是,MIN(继承方式,基类访问方式) 实际基类的private成员在派生类不可见,只是语法上无法访问,假如你调用基类的方法还是可以用的。 但是调用继承的方法可以用 使用关键字c

qq_45928272的博客 956

【多态】有关多继承和菱形继承的多态

博主会持续更新本篇文章主要讲解多继承和菱形继承的多态的相关内容。

有趣的中国人的博客 1493

C++继承进阶篇:深入菱形继承与虚拟继承(三)

C++菱形继承与虚拟继承解析 本文深入探讨了C++中菱形继承的问题及其虚拟继承解决方案。主要内容包括: 继承类型:单继承和多继承的区别,菱形继承作为多继承的特殊情况 菱形继承问题: 数据冗余:派生类中存在多份基类成员 二义性:无法直接确定访问哪个路径的成员 虚拟继承解决方案: 使用virtual关键字声明虚拟继承 消除数据冗余和二义性问题 实现原理: 虚基表指针和虚基表机制 通过偏移量访问共享基类成员 实际内存布局分析 代码验证:通过实际代码展示虚拟继承的内存布局变化 虚拟继承虽然解决了菱形继承问题,但增加

2302_79947392的博客 984

一文带你掌握多继承,菱形继承以及虚拟继承

介绍C++中的多继承,菱形继承,虚继承. 分析虚继承的原理.

@初阶牛的博客 1469

C++:菱形继承与菱形虚拟继承

什么是菱形继承?菱形继承的问题是什么? 什么是菱形虚拟继承?如何解决数据冗余和二义性的 继承和组合的区别?什么时候用继承?什么时候用组合

rab1211764820的博客 915

C++继承之菱形继承

在学习菱形继承之前,我们先了解两个概念:单继承、多继承。 单继承 一个子类只有一个直接父类时称这个继承关系为单继承。 多继承 一个子类有两个或以上直接父类时称这个继承关系为多继承。 菱形继承 菱形继承是多继承的一种特殊情况。 接下来我们来看一个具体的对象成员模型构造: class Person { public: string _name; // 姓名 }; class Father ...

xupeng1644的博客 2610
上一篇: C++之继承
下一篇: C++之多态
coolnut12138
博客等级 码龄9年 51粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值