C++:多继承虚继承

news2024/12/23 13:34:56

在C++中,虚继承(Virtual Inheritance)是一种特殊的继承方式,用于解决菱形继承(Diamond Inheritance)问题。菱形继承指的是一个类同时继承自两个或更多个具有共同基类的类,从而导致了多个实例同一个基类的实例的冗余。

多继承-菱形继承

现在来思考以下经典的菱形继承问题:

     Animal
      / \
    cat  bear
      \ /
      panda

在这个继承结构中,类 Animal 是基类,类 catbear 都直接继承自 Animal,而类 panda 继承自 catbear。当使用传统的继承方式时,panda 类将会继承两份 Animal 类的实例,这会导致冗余。

以下代码就是典型的菱形继承的例子:
class Animal
{
public:
    Animal();
    ~Animal();
    int m_nAge;
};
​
​
class Cat : public Animal
{
public:
    Cat();
    ~Cat();
};
​
class bear:public Animal
{
public:
    bear();
    ~bear();
};
​
class Panda:public Cat,public bear
{
public:
    Panda();
    ~Panda();
};

AnimalAnimal 类是一个基类,具有一个 m_nAge 成员变量,用于表示动物的年龄。

Cat 类和 bearCat 类和 bear 类分别是 Animal 类的公有派生类。它们继承了 Animal 类的成员变量和方法。

PandaPanda 类同时公有派生自 Cat 类和 bear 类,从而继承了这两个类的成员。由于 Cat 类和 bear 类都是从 Animal 类继承而来的,因此 Panda 类实际上包含了两份 Animal 类的成员变量 m_nAge,这导致冗余数据。

在构造函数中设置标志,创建panda对象后得到的结果:创建一个Panda对象,Animal构造了两次。

此时我在主程序中进行panda对象的创建,并尝试对panda继承下来的的m_nAge对象进行赋值:

此时程序报错,并提示Pandam_nAge成员不明确,原因其实就是因为Panda 继承自Catbear两个类,那么就意味着Panda类继承了两份 Animal 类的成员变量 m_nAge,此时使用panda->m_nAge = 10;Panda的变量进行赋值编译器就会混淆;如果不想程序爆红就在赋值时就必须成员变量的继承来源:

int main() {
    Panda * panda = new Panda;
    //成员变量的赋值
    panda->Cat::m_nAge = 10;
    panda->bear::m_nAge = 15;
​
    std::cout << "Panda Age:" << panda->Cat::m_nAge << "  Panda AgeB:" << panda->bear::m_nAge << std::endl;
    delete panda;
    system("pause");
    return 0;
}

panda->Cat::m_nAge = 10;:给 panda 对象中 Cat 类的成员变量 m_nAge 赋值为 10

panda->bear::m_nAge = 15;:给 panda 对象中 bear 类的成员变量 m_nAge 赋值为 15

就算解决了程序不报错的问题其实菱形继承(多继承)时还会出现其他问题:

1.数据冗余:每个派生类实例都会包含基类实例的副本,这可能会导致内存空间的浪费,特别是在多重继承的情况下,可能会存在多份相同的基类数据。
2.数据一致性:如果多个派生类实例共享同一个基类实例,那么当修改其中一个派生类实例中的基类数据时,会影响到其他共享同一基类实例的派生类实例,这可能会导致数据不一致的问题。
3.维护困难:当多个派生类实例共享同一个基类实例时,可能会增加代码的复杂性和维护成本,因为需要确保在任何修改基类数据的地方都能正确地处理多个派生类实例。
4.生命周期问题:如果派生类实例在不同的时间点创建和销毁,而共享的基类实例在某个派生类实例被销毁后还继续存在,这可能会导致悬挂指针或内存泄漏等问题

这个时候就可以使用虚继承对以上问题进行统一解决;

虚继承

虚继承通过使用关键字 virtual 来解决这个问题。当一个类以虚继承方式继承自一个基类时,无论该类被多次继承,其基类的实例只会在继承层次结构中的最顶层被创建一次。这意味着,对于上面的菱形继承问题,使用虚继承后,D 类将只继承一份 A 类的实例。

回到上述例子:

class Animal
{
public:
    Animal();
    ~Animal();
​
    int m_nAge;
}
​
//使用虚继承
class Cat : public virtual Animal
{
public:
    Cat();
    ~Cat();
};
​
//使用虚继承
class bear:public virtual Animal
{
public:
    bear();
    ~bear();
​
};
​
class Panda:public Cat,public bear
{
public:
    Panda();
    ~Panda();
​
};

Cat 类和 bear:这两个类都使用了虚继承,即使用 virtual 关键字来继承自 Animal 类。这意味着无论 Cat 类和 bear 类被多次继承,Animal 类的实例都只会在继承层次结构中的最顶层被创建一次。这样可以避免在 Panda 类中出现多个 Animal 类的实例,从而解决了菱形继承问题。

PandaPanda 类同时公有派生自 Cat 类和 bear 类,从而继承了这两个类的成员。因为 Cat 类和 bear 类都是虚继承自 Animal 类的,所以 Panda 类中只包含了一个 Animal 类的实例,从而避免了多个实例共享同一个基类实例的冗余问题。

这就意味着此时我们在主函数中创建panda对象,并对对象的m_nAge成员进行赋值/访问:

int main() {
    Panda * panda = new Panda;
    panda->m_nAge = 10;
    std::cout << "Panda Age:" << panda->m_nAge << std::endl;
    delete panda;
}

运行结果:

Panda Age:10

panda对象中的m_nAge成员变量就只有一个副本,这个时候就对对象中的成员进行访问时就无需再指定继承路径了。

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

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

相关文章

Apache POI入门学习

Apache POI入门学习 官网地址 excel中使用到的类读取excel表格内容表格内容maven依赖方式一测试结果 方式二测试结果 向excel中写入数据方式一方式二方式三测试结果 从 Excel 工作表中的公式单元格读取数据测试结果 Excel 工作表中写入公式单元格从受密码保护的Excel中读取数据…

opencv图片的平移-------c++

图片平移 cv::Mat opencvTool::translateImage(const cv::Mat& img, int dx, int dy) {// 获取图像尺寸int rows img.rows;int cols img.cols;// 定义仿射变换矩阵cv::Mat M (cv::Mat_<float>(2, 3) << 1, 0, dx, 0, 1, dy);// 进行仿射变换cv::Mat dst;cv…

Linux-信号概念

1. 什么是信号 信号本质是一种通知机制&#xff0c;用户or操作系统通过发送信号通知进程&#xff0c;进程进行后续处理 在日常生活中就有很多例子&#xff0c;比如打游戏方面王者荣耀的“进攻”&#xff0c;“撤退”&#xff0c;“请求集合”&#xff0c;“干得漂亮&#xff01…

第一篇:刚接触测试你应该知道什么

欢迎你接触软件测试这一行业。 刚接触它时&#xff0c;你肯定或多或少会有疑惑&#xff0c;我该做什么&#xff1f;大家口口相传的软件测试就是 【点点点】 真的是你日常的工作吗&#xff1f; 那么本文我将陪你一起&#xff0c;对我们刚接触到测试这个工作以后&#xff0c;应该…

第七节课《OpenCompass司南--大模型评测实战》

OpenCompass 大模型评测实战_哔哩哔哩_bilibili https://github.com/InternLM/Tutorial/blob/camp2/opencompass/readme.md InternStudio 一、通过评测促进模型发展 面向未来拓展能力维度&#xff1a;评测体系需增加新能力维度&#xff08;数学、复杂推理、逻辑推理、代码和…

[SUCTF 2019]CheckIn 1

解题步骤 上传木马图片&#xff0c;命名为b.php GIF89a <script languagephp>eval($_POST[cmd])</script>bp抓包&#xff0c;修改数据&#xff1b;然后可看到上传的文件 上传.user.ini文件&#xff0c;内容编写如下&#xff0c;然后bp抓包修改文件类型 GIF8…

MOS产品在光伏逆变器上的应用和产品选型

2023年全球光伏装机量表现优异&#xff0c;根据BloombergNEF统计数据&#xff0c;2023年全球光伏新增装机量444GW&#xff0c;同比增长76.2%&#xff0c;其中约一半新增装机量来自中国。 中国光伏新技术迭代不断&#xff0c;产业链降本增效加速。根据CPIA数据&#xff0c;2022年…

简述 BIO 、NIO 模型

BIO : 同步阻塞I/O&#xff08;Block IO&#xff09; 服务器实现模式为每一个连接一个线程&#xff0c;即客户端有连接请求时服务器就需要启动一个线程进行处理&#xff0c;如果这个连接不做任何事情会造成不必要的线程开销&#xff0c;此处可以通过线程池机制进行优化。 impo…

新的项目springboot

buybuyshenglombok <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency> 添加依赖 lombok package com.example.demo.pojo;import lombok.AllArgsConstructor; import lombok.Data; import …

【机器学习与实现】线性回归分析

目录 一、相关和回归的概念&#xff08;一&#xff09;变量间的关系&#xff08;二&#xff09;Pearson&#xff08;皮尔逊&#xff09;相关系数 二、线性回归的概念和方程&#xff08;一&#xff09;回归分析概述&#xff08;二&#xff09;线性回归方程 三、线性回归模型的损…

BigDecimal:踩坑

问题描述 两个BigDecimal相除, 抛了异常 原因分析&#xff1a; Java 中使用 BigDecimal 做除法运算的时候&#xff0c;值有可能是无限循环的小数&#xff0c;结果是无限循环的小数&#xff0c;就会抛出上面这个异常。 来看看源码&#xff1a; public BigDecimal divide(BigD…

马常旭新歌《如愿》:音乐界的“旭日”再现

在这个春暖花开的季节&#xff0c;音乐界又迎来了一股清新的“旭日”气息。是的&#xff0c;就在2024年4月17日&#xff0c;马常旭的新歌《如愿》&#xff08;旭日版&#xff09;在网易云音乐上线了&#xff01;一年的等待&#xff0c;终于迎来了他的音乐回归&#xff0c;给我们…

【3D基础】坐标转换——地理坐标投影到平面

汤国安GIS原理第二章重点 1.常见投影方式 https://download.csdn.net/blog/column/9283203/83387473 Web Mercator投影&#xff08;Web Mercator Projection&#xff09;&#xff1a; 优点&#xff1a; 在 Web 地图中广泛使用&#xff0c;易于显示并与在线地图服务集成。在较…

在echarts中使用geojson地图

以中国地图为例 先看效果 代码实现&#xff1a; <div id"refChinaMap" :style"{ width: 75%, height: 100% }"></div>import * as echarts from "echarts"; import ChinaJSON from "./chinaMap.json";const initChinaMa…

场景文本检测识别学习 day09(Swin Transformer论文精读)

Patch & Window 在Swin Transformer中&#xff0c;不同层级的窗口内部的补丁数量是固定的&#xff0c;补丁内部的像素数量也是固定的&#xff0c;如上图的红色框就是不同的窗口&#xff08;Window&#xff09;&#xff0c;窗口内部的灰色框就是补丁&#xff08;Patch&#…

【Linux网络编程】自定义协议+HTTP协议

【Linux网络编程】自定义协议HTTP协议 目录 【Linux网络编程】自定义协议HTTP协议协议定制&#xff0c;序列化和反序列化应用层中的HTTP认识URL&#xff08;网址&#xff09;urlencode和urldecodeHTTP协议格式使用telnet获取百度的根目录资源HTTP的方法表单 HTTP的状态码HTTP常…

一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?

目录 1.Redis 分布式锁 &#xff08;1&#xff09;Redis 最普通的分布式锁 &#xff08;2&#xff09;RedLock 算法 2.zk 分布式锁 3.redis 分布式锁和zk分布式锁的对比 1.Redis 分布式锁 官方叫做 RedLock 算法&#xff0c;是 Redis 官方支持的分布式锁算法。 这个分布式…

[qnx] 通过zcu104 SD卡更新qnx镜像的步骤

0. 概述 本文演示如果给Xlinx zcu104开发板刷入自定义的qnx镜像 1.将拨码开关设置为SD卡启动 如下图所示&#xff0c;将1拨到On,2,3,4拨到Off&#xff0c;即为通过SD启动。 2.准备SD卡中的内容 首先需要将SD格式化为FAT32的&#xff08;如果已经是FAT32格式&#xff0c;则…

力扣每日一题114:二叉树展开为链表

题目 中等 提示 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…

sass-loader和node-sass与node版本的依赖问题

sass-loader和node-sass与node版本的依赖问题 没有人会陪你走到最后&#xff0c;碰到了便是有缘&#xff0c;即使到了要下车的时候&#xff0c;也要心存感激地告别&#xff0c;在心里留下空白的一隅之地&#xff0c;多年后想起时依然心存甘味。——林清玄 报错截图 报错信息 np…