C++——菱形继承和虚继承

news2024/11/24 4:12:03

0.关注博主有更多知识

C++知识合集

目录

1.什么是菱形继承和虚继承

2.菱形继承所带来的问题

3.虚继承的解决方案

3.1虚基表

4.继承与组合

菱形继承和虚继承本身就是一个"bug",甚至在C++程序员当中有"谁用谁尚阿比"的说法。至于为什么要谈菱形继承和虚继承,那就是因为面试官要问。

1.什么是菱形继承和虚继承

C++作为"第一个吃螃蟹的人",勇敢地设计出了多继承的语法,多继承出现之后,由于一些顶尖程序员的脑洞非常大,就发现了菱形继承所带来数据冗余和二义性的问题,C++标准委员会为了解决这个问题,就设计出了虚继承。从此之后,后面"抄作业的人"就没有多继承的语法,例如java。

2.菱形继承所带来的问题

先理解一段简单的代码:

/*B、C继承自A---D继承自B、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._a = 3; // 报错,_a不明确
        d.B::_a = 3;
        d.C::_a = 8;
        return 0;
}

这段代码的调试结果为:

这就很好解释了二义性的问题,因为在D类对象当中存在了两份A类对象,所以要访问D类对象中的A类对象时必须指明访问,否则就会触发二义性。如果在某些应用场景中,两份A类对象确实是多余的,那么就又触发了数据冗余问题。所以菱形继承存在数据冗余和二义性的问题。下面给出这段程序的继承关系示意图和D类对象模型示意图:

3.虚继承的解决方案

在介绍如何解决菱形继承的问题之前,先理解一段简单的虚拟单继承的代码:

class A
{
public:
        int _a = 1;
};
class B : virtual public A // virtual为虚继承关键字
{
public:
        int _b = 2;
};
int main()
{
        B b;
        return 0;
}

调试-内存窗口截图如下:

如上图所示,B类对象中的A类对象不再存储成员变量,而是存储一个未知值,这个位置本应该存储A类对象的成员变量,但是A类的成员变量却跑到了B类对象的最后。如此类推,如果再有一个C类虚继承自A类,那么C类对象模型也应该像上图一样。

解决菱形继承的方案就是在继承体系的"腰部"使用虚继承,以下面这段代码为例:

class A
{
public:
    int _a = 1;
};
class B : virtual public A
{
public:
    int _b = 2;
};
class C : virtual public A
{
public:
    int _c = 3;
};
class D : public B, public C
{
public:
    int _d = 4;
};

int main()
{
    D d;
    /*都不报错了,他们操作的都是同一个_a*/
    d._a = 1;
    d.B::_a = 3;
    d.C::_a = 8;
    return 0;
}

最终调试的结果如下:

 

不要被监视窗口所误导,上图三个红色箭头所指向的_a实际上是同一个_a,也就是说D类对象的模型当中只存在一份A类对象了。

通过内存窗口观察D类对象的模型:

 

与之前介绍的一样,B类对象和C类对象当中本该存储A类对象的位置存储了一个随机值。实际上这个随机值是一个指针,它指向了虚基表。

3.1虚基表

对于上面的图片,介绍了所谓的"随机值"是指针,指向了一个名为虚基表的东西,那么再另起一个内存窗口,观察虚基表的构成:

 

由此可见,虚基表存储的有效内容为偏移量,具体的来说,当某一指针或引用指向D类对象时,需要访问_a时,就需要通过虚基表当中的偏移量来确定访问目标的位置。虽然虚基表的存在增加了几次指针的运算,但是试想以下,如果A类对象足够大,在菱形继承体系中不使用虚继承,那么最终的D类对象就会有两份A类对象,并且A类对象是一个巨大的对象,那么如果使用了虚继承,就能将两份A类对象压缩成一份A类对象。

所以使用虚继承,能够解决菱形继承带来的数据冗余和二义性问题。最后以一张图描述D类对象的模型:

 

4.继承与组合

组合的类设计方式是这样的:

class A
{
public:
        int _a;
};
class B
{
public:
        A a;
};

可以明显看出与继承的差别:组合的耦合度更低,继承的耦合度更高。实际上在真实的设计环境当中是很忌讳高耦合的,但是某些场景当中却不得不这么做。

继承是一种is-a的关系,例如下面这个例子:

class Person
{};
class Student : public Person
{};

这个例子所表达的意思就是Student是Person,即学生是人。

组合是一种has-a的关系,例如最开头的那段代码,表达的意思就是B类对象当中有一个A类对象。

针对不同的场景使用不同的复用手段,当条件只允许使用is-a的关系时就使用继承;只允许使用has-a的关系时就使用组合;当既可以使用继承又可以使用组合的关系时使用组合。

为什么要尽量使用组合关系?

因为对于继承来说,它相当于一种白箱复用,即箱子里面的内容能够清清楚楚的看到;对于组合来说,它相当于一种黑箱复用,即箱子里面的内容大多是不可见的,能够看见的也仅仅是一部分(例如设计类时提供给外部的成员函数)。对于继承来说,如果基类的非private成员发生了变动,由于耦合度高的原因,派生类也将会受到影响;对于组合来说,被包含的对象只有public成员发生变动时,才有可能影响到包含该对象的对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[bugfix]解决visual studio installer双击后进度条一闪而过之后无反应的问题

问题描述: 源于跑一个神经网络代码,跑着跑着说需要microsoft visual C 14.0版本及其以上,然而我苦于根本下不了microsoft visuall C包的状态啊,而且点它没反应这件事已经持续了1年左右,因为没太耽误我做事我就一直没管…

21 条法则助你“玩转”分库分表

好好的系统,为什么要分库分表? 我们结合具体业务场景,以t_order表为例进行架构优化。由于数据量已经达到亿级别,查询性能严重下降,因此我们采用了分库分表技术来处理这个问题。具体而言,我们将原本的单库分…

java生成随机数

文章目录 java生成随机数导入包生成一个随机数生成一个 [ 0 , b o u n d ) \color{red}{[0,bound)} [0,bound)的随机数生成一个 20 \color{red}{20} 20到 90 \color{red}{90} 90的随机数总结现在尝试生成 − 10 \color{red}{-10} −10到 10 \color{red}{10} 10之间的随机数 ja…

《最新出炉》Python+Playwright自动化测试-2-playwright的API及其他知识

一.简介 上一篇我已经将PythonPlaywright的环境搭建好了,而且也简单的演示了一下三款浏览器的启动和关闭,是不是很简单啊。今天主要是把一篇的中的代码进行一次详细的注释,然后说一下playwright的API和其他相关知识点。那么首先将上一篇中的…

MyBatis——MyBatis注解开发

MyBatis编写SQL除了使用Mapper.xml还可以使用注解完成。当可以使用Auto Mapping时使用注解非常简单,不需要频繁的在接口和mapper.xml两个文件之间进行切换。但是必须配置resultMap时使用注解将会变得很麻烦,这种情况下推荐使用mapper.xml进行配置。 MyB…

问题解决:cmd中创建文件夹被拒绝访问。

问题: 在cmd中准备创建一个B盘node.js文件夹下的一个node_global文件被拒绝访问出错。 Microsoft Windows [版本 10.0.19045.2965] (c) Microsoft Corporation。保留所有权利。C:\Users\SueMagic>md B:\nodejs\node_global 拒绝访问。C:\Users\SueMagic>原因…

Learning C++ No.26 【深入学习位图】

引言: 北京时间:2023/5/30/15:30,刚睡醒,两点的闹钟,硬是睡到了2点40,那种睡不醒的感觉,真的很难受,但是没办法,欠的课越来越多,压的我喘不过气了都&#xf…

华为OD机试真题B卷 Java 实现【整理扑克牌】,附详细解题思路

一、题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1 对扑克牌进行分组,形成组合牌,规则如下: 当牌面数字相同张数大于等于4时&a…

【Python Selenium】零基础也能轻松掌握的学习路线与参考资料

Python Selenium是一种基于Python编程语言的自动化测试框架,用于Web应用程序的测试和自动化。Python Selenium是一个非常流行的工具,它可以通过模拟用户行为来测试Web应用程序,同时还可以通过Python编写脚本实现自动化测试,并且可…

Spring Boot如何实现自定义Starter?

Spring Boot如何实现自定义Starter? 在 Spring Boot 中,Starter 是一种特殊的依赖,它可以帮助我们快速地集成一些常用的功能,例如数据库连接、消息队列、Web 框架等。在本文中,我们将介绍如何使用 Spring Boot 实现自…

python视频图片美化

python视频图片美化 git clone https://github.com/s0md3v/roop.git If you aren’t good with following commands, here’s a video tutorial install python (and pip too if neeed) install git install ffmpeg If you are on Windows, install “Microsoft Visual C 14”…

Cadence OrCAD Capture 搜索的到的元器件无法在图纸中找到的问题

🏡《总目录》 目录 1,概述2,问题现象3,问题原因解决方案4,总结B站关注“硬小二”浏览更多演示视频 1,概述 本文简单介绍在使用Capture绘图时,搜索元器件或其他对象,存在搜索结果,但在图纸中无法找到的原因和解决方案。 2,问题现象 如下图所示搜索U20后,存在搜索结果…

javascript基础十二:JavaScript中的事件模型如何理解?

一、事件与事件流 javascript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等 由于DOM是一个树结构,如果在父子节点绑定事件时候,当触…

浅析Redis集群数据倾斜问题及解决方法

概 述 在服务端系统服务开发中,缓存是一种常用的技术,它可以提高系统对请求的处理效率,而redis又是缓存技术栈中的一个佼佼者,广泛的应用于各种服务系统中。在大型互联网服务中,每天需要处理的请求和存储的缓存数据…

【Java系列】Mybatis-Plus 使用方式介绍

1 Mybatis-Plus简介 Mybatis-Plus 提供了多种方式来执行 SQL,包括使用注解、XML 映射文件和 Lambda 表达式等。其中,使用 Lambda 表达式是 Mybatis-Plus 推荐的方式,因为它更加直观和类型安全。 2 使用方法 1 Lambda 表达式执行 SQL 以下是…

pix2pixHD---loss---损失函数

在Pix2PixHDModel代码中首先定义损失: 首先看第一个:输入的两个参数use_gan_feat_loss, use_vgg_loss默认为false,则前缀有not,所以两个参数都是True。 def init_loss_filter(self, use_gan_feat_loss, use_vgg_loss):flags (Tr…

PCIE知识点-022:PCIe 参考时钟结构

图1:参考时钟结构示意图[4] 1. Common Refclk Architecture Common Refclk Architecture,即同源参考时钟架构,PCIe收发设备共用一个时钟源,是目前是使用最为广泛的方案。 缺点: 对于适用同一 Common Clock 作为参考时…

第四章 运行时数据区

文章目录 前言一、🚗 双亲委派机制1、 问题的引出:是否会被外来程序对系统进行破坏2、总结3、双亲委派的优势4、沙箱安全机制5、其他 二、🚒 运行时数据区线程 三、🛺 PC 寄存器概述(记录下一条程序指令的地址&#xf…

Vulnhub: dpwwn: 1靶机

kali:192.168.111.111 靶机:192.168.111.131 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.131 爆破出mysql的root用户为空密码 hydra -l root -P /usr/share/wordlists/rockyou.txt 192.168.111.131 -s 3306 mysq…

UI自动化 Xpath定位必知必会

目录 javascript xpath定位 定位单个元素: 定位多个元素: 验证xpath定位语法是否OK 尽量使用模糊匹配定位元素 模糊匹配contains 使用关联文本值定位 text() 在UI自动化测试中用的最频繁的就是xpath定位了,所以用好xpath定位至关重要&…