C++:多重继承带来的问题及解决方法

在继承的过程中,如果一个派生类由多个基类派生,则称为多重继承(实际开发中会引入困难,不建议使用)

目录

多重继承的构造函数、析构函数调用顺序:

多重继承引发的二义性问题:

问题1:继承同名成员:

问题2:菱形继承(钻石继承)

多重继承的解决方法:

1.虚继承(virtual)

2.虚继承的二义性

语法:

class 派生类类名:继承方式1 基类名1 , 继承方式2 基类名2 ...
{}

关系图:

多重继承的构造函数、析构函数调用顺序:

构造函数:顺序与单继承一致,先基类、组合类、派生类新增加成员;

强调:

1、 同一层基类的构造函数顺序取决于派生类声明继承基类时的基类先后顺序,与构造函数定义成员初始化列表顺序无关

class A :public B,public C,public D
//基类的构造函数执行顺序为:B C D
{
public:A (int d,int c,int b,int a):D(d),C(c),B(b),m_a(a)
{cout << "A(int)构造函数" << endl;}
//与构造函数初始化列表顺序无关~A(){cout << "~A()析构函数" <<  endl;}
}

输出:

B(int)构造函数

C(int)构造函数

D(int)构造函数

A(int)构造函数

~A()析构函数

~D()析构函数

~C()析构函数

~B()析构函数

2、组合类的成员对象的构造函数执行顺序取决于成员对象的声明顺序,与构造函数的初始化列表排列顺序无关

class A:public B
{
private:C c1;D d1;int a1;//取决于成员对象的声明顺序
public://与初始化列表成员顺序无关A(int a,int b,int d,int c):a1(a),B(b),c1(c),d1(d){cout << "A(int)构造函数" << endl;}~A(){cout << "~A()析构函数" <<  endl;}
//...
}

输出:

B(int)构造函数

C(int)构造函数

D(int)构造函数

A(int)构造函数

~A()析构函数

~D()析构函数

~C()析构函数

~B()析构函数

析构函数:顺序与构造函数相反,先构造的后继承,形成对称

多重继承引发的二义性问题:

问题1:继承同名成员:

派生类继承多基类的同名成员

eg:

class A
{
public :
void show()
{cout<<"A show()"<<endl;}
}class B
{
public :
void show()
{cout<<"B show()"<<endl;}
}class C
{
public :
void showc()
{cout<<"C showc()"<<endl;}
}

引入问题:派生类中出现继承而来的同名成员,导致无法直接引用

int main()
{C c;//error://c.show()  //二义性:不知道此时c 该执行A类的show还是B类的show函数return 0;
}

解决方法:添加作用域标识符,消除成员限定的二义性

int main()
{C c;//解决方法:c.A::show();c.B::show();c.showc();return 0;
}

作用域格式:

对象名.基类名::成员变量名;
对象名.基类名::成员函数名(参数列表);

问题2:菱形继承(钻石继承)

两次继承三代成员:由一基类派生了两个第一代派生,而一类又共同继承了这两个一代派生形成二代派生

关系图:

class A //间接基类,第一层的类
{
protected:int m_a;
};class B :public A //直接基类B
{
protected:int m_b;
//继承m_a;
};class C :public A //直接基类C
{
protected:int m_c;
//继承m_a;
};

问题引入:

class D:public C,public B
{
public:
void set(int a)
{//继承m_a;//error:  m_a=a;//不知道赋值给B 类的还是C类的m_a;
}
...

解决方法:

void set(int a)
{B::m_a=a;C::m_a=a;
}
protected:int m_d;
//继承 m_a 源于B;
//继承 m_b 源于B;
//继承 m_a 源于C;
//继承 m_c 源于C;};int main()
{D d;return 0;
}

注意:

作用域标识符虽然可以解决二义性问题,但是在派生类中产生两个同名成员,增加了内存开销,解决方法间下方虚继承

多重继承的解决方法:

解决多重继承的命名冲突及数据冗余问题,C++中虚继承:使派生类只保留一份间接基类成员

1.虚继承(virtual)

格式:

class      A   : virtual  public    B
class 派生类类名:virtual 继承方式 基类类名

使用引例:

//多重继承 虚继承
#include <iostream>
#include <string>
using namespace std; class A
{
public :int m_a;
};class B:virtual public A
{
public:int m_b;//虚继承 m_a
};class C :virtual public A
{
public:int m_c;//虚继承 m_a
};class D:virtual public A
{
public:int m_d;//虚继承 m_a
};class E :public C, public B
{
public :int m_e;//虚继承 m_a//继承 m_b//继承 m_cvoid seta(int a) { m_a = a; }//正确void setb(int b) { m_b = b; }//正确void setc(int c) { m_c = c; }//正确void setd(int d) { m_d = d; }//正确void setd(int e) { m_e = e; }//正确
};int main()
{E e;return 0;
}

代码关系梳理:

虚继承(在继承的基类前加入virtual关键字),可以让声明虚继承的类作出声明,共享其基类(虚基类:Virtual Base Class),使得不论虚基类 class A 被继承了多少次,派生类中只含有一份虚基类成员 m_a,节省内存空间,解决了成员名的歧义问题。

局限:

派生类只影响从指定的虚基类的派生类进一步派生出的后代类,而不会影响一代派生类本身

在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。通常情况下,使用虚继承的类层次是由一个人或者一个项目组一次性设计完成的。对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。

C++标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。

using istream  = basic_istream<char, char_traits<char>>;
using ostream  = basic_ostream<char, char_traits<char>>;
using iostream = basic_iostream<char, char_traits<char>>;
class basic_istream  : virtual public basic_ios;
class basic_ostream  : virtual public basic_ios;
class basic_iostream : public basic_istream,
public basic_ostream;

2.虚继承的二义性

虚继承要求在派生类中只包含一份虚基类数据,该数据在直接访问时无二义性问题

以菱形继承为例,判断是否存在其他二义性问题?

  1. 仅A 虚基类中定义成员变量 x, 派生类D 直接访问 A类的x 成员(无二义性问题)
  2. A、B类或 A、C 类中均含有成员变量x ,派生类访问 x 成员时,派生类x 优先级高于虚基类成员 x (无二义性)
  3. A、B、C 中三个类均含有变量x ,派生类D访问时,B、C 类成员优先级一致(存在二义性问题)作用域标识符区分

class A //间接基类
{
protected:int x;
};class B :virtual public A //直接基类B
{
protected:int x;
};class C :virtual public A //直接基类C
{
protected:int x;
};class D :public B, public C //派生类D
{
public:void setx(int a) {//x = a;//错误,有二义性//添加作用域标识符B::x = a;//正确C::x = a;//正确}
};

总结:

可以看到,使用多继承经常会出现二义性问题,必须十分小心。上面的例子是简单的,如果继承的层次再多一些,关系更复杂一些,程序员就很容易陷人迷魂阵,程序的编写、调试和维护工作都会变得更加困难,因此不提倡在程序中使用多继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多继承,能用单一继承解决的问题就不要使用多继承。也正是由于这个原因,C++ 之后的很多面向对象的编程语言,例如Java、C#、PHP等,都不支持多继承。

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

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

相关文章

【SVN】使用TortoiseGit删除Git分支

使用TortoiseGit删除Git分支 前言 平时我在进行开发的时候&#xff0c;比如需要开发一个新功能&#xff0c;这里以蘑菇博客开发服务网关-gateway功能为例 一般我都会在原来master分支的基础上&#xff0c;然后拉取一个新的分支【gateway】&#xff0c;然后在 gateway分支上进…

SQL Server添加用户登录

我们可以模拟一下让这个数据库可以给其它人使用 1、在计算机中添加一个新用户TeacherWang 2、在Sql Server中添加该计算机用户的登录权限 exec sp_grantlogin LAPTOP-61GDB2Q7\TeacherWang -- 之后这个计算机用户也可以登录数据库了 3、添加数据库的登录用户和密码&#xff0…

微信小程序-底层框架-开发文档学习笔记

查看更多学习笔记&#xff1a;GitHub&#xff1a;LoveEmiliaForever 微信小程序开发指南 微信小程序开发文档 双线程模型 小程序是基于双线程模型的&#xff0c;在这个模型中&#xff0c;小程序的逻辑层与渲染层分开在不同的线程运行 技术选型 在对小程序的架构设计时的要求…

YOLOv7代码解读[02] cfg/training/yolov7.yaml解读

ELAN结构 MP结构 SPPCSPC结构 ELAN-H结构 # parameters nc: 80 # number of classes depth_multiple: 1.0 # model depth multiple width_multiple: 1.0 # layer channel multiple# anchors anchors:- [12,16, 19,36, 40,28] # P3/8- [36,75, 76,55, 72,146] #…

UnityWebGL UGUI中文不显示问题

这是Unity编辑中效果 打包成webgl后的效果&#xff08;中文没有显示出来&#xff09; 解决方法 将Unity默认使用的Arial替换成中文字体。 1.找到电脑字体库&#xff08;win电脑字体库路径&#xff1a;C:\Windows\Fonts &#xff1b;Mac电脑搜索“字体册”&#xff09;。 2.将…

大数据职业技术培训包含哪些

技能提升认证考试&#xff0c;旨在通过优化整合涵盖学历教育、职业资格、技术水平和高新技术培训等各种教育培训资源&#xff0c;通过大数据行业政府引导&#xff0c;推进教育培训的社会化&#xff0c;开辟教育培训新途径&#xff0c;围绕大数据技术人才创新能力建设&#xff0…

【GPTs分享】每日GPTs分享之Image Generator Tool

今日GPTs分享&#xff1a;Image Generator Tool。Image Generator Tool是一种基于人工智能的创意辅助工具&#xff0c;专门设计用于根据文字描述生成图像。这款工具结合了专业性与友好性&#xff0c;鼓励用户发挥创造力&#xff0c;同时提供高效且富有成效的交互体验。 主要功能…

Vue.js+SpringBoot开发校园失物招领管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系统公告模块2.4 感谢留言模块 三、界面展示3.1 登录注册3.2 招领模块3.3 寻物模块3.4 公告模块3.5 感谢留言模块3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 校园失物招领…

【51单片机】红外遥控红外遥控电机调速(江科大)

1.红外遥控简介 红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出 通信方式:单工,异步 红外LED波长:940nm 通信协议标准:NEC标准 2.硬件电路 红外发送部分 IN高电平时&#xff0c;LED不亮&#xff0c;IN低电平时&…

STM32学习3 寄存器映射和GPIO寄存器编程

STM32学习3 寄存器映射和GPIO寄存器编程 一、STM32外设内存空间1. 内存空间划分2. 区域功能说明&#xff08;1&#xff09;block0&#xff08;2&#xff09;block1&#xff08;3&#xff09;block2&#xff08;4&#xff09;block3~4&#xff08;5&#xff09;block5&#xff0…

UE4 材质多张图片拼接成一张图片(此处用2×2拼接)

UE4 材质多张图片拼接成一张图片&#xff08;此处用22拼接&#xff09; //TexCoord,TextureA,TextureB,TextureC,TextureDfloat3 ReturnTexture TextureA; if(TexCoord.x < 0.5 && TexCoord.y < 0.5) {ReturnTexture TextureA; } else if(TexCoord.x > 0.5…

对Redis锁延期的一些讨论与思考

上一篇文章提到使用针对不同的业务场景如何合理使用Redis分布式锁&#xff0c;并引入了一个新的问题 若定义锁的过期时间是10s&#xff0c;此时A线程获取了锁然后执行业务代码&#xff0c;但是业务代码消耗时间花费了15s。这就会导致A线程还没有执行完业务代码&#xff0c;A线程…

vue中循环多个li(表格)并获取对应的ref

有种场景是这样的 <ul><li v-for"(item,index) in data" :key"index" ref"???">{{item}}</li> </ul> //key值在项目中别直接用index&#xff0c;最好用id或其它关键值const data [1,2,3,4,5,6]我想要获取每一个循环并…

华为云是什么

公有云配置 区域&#xff1a; 同一个区域中的云主机是可以互相连通的&#xff0c;不通区域云主机是不能使用内部网络互相通信的 选择离自己比较近的区域&#xff0c;可以减少网络延时卡顿 华为云yum仓库&#xff1a;https://repo.huaweicloud.com/rockylinux/ 首先完成跳板机的…

【linux进程信号(一)】信号的概念以及产生信号的方式

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 进程信号 1. 前言2. 信号的基…

亿道推出重磅加固平板!为行业发展注入新动力

随着科技生产力的不断发展&#xff0c;各行各业都得到质的飞跃。产品的迭代速度也大大加快&#xff0c;作为全球领先的加固行移动终端一站式提供商&#xff0c;亿道信息跟紧时代潮流&#xff0c;推出EM-I10J、EM-I20J两款均衡型加固平板&#xff0c;为行业发展注入新动力。 接地…

YOLOv8 DeepSORT实现智能交通监控-改进yolo单目测距及速度测量-流量计数

YOLOv8&#xff1a;目标检测算法详解 YOLO&#xff08;You Only Look Once&#xff09;系列是一种单阶段、实时的目标检测框架&#xff0c;其最新迭代版本YOLOv8继承并优化了前代YOLO在速度与精度上的优势。YOLOv8的核心思想在于将整幅图像一次性输入到神经网络中&#xff0c;直…

dpdk协议栈之udp架构优化

dpdk优势 传统网络架构与 DPDK&#xff08;Data Plane Development Kit&#xff09;网络架构之间存在许多区别&#xff0c;而 DPDK 的优势主要体现在以下几个方面&#xff1a; 数据包处理性能&#xff1a;传统网络架构中&#xff0c;网络数据包的处理通常由操作系统的网络协议…

【学习笔记】Serdes中的高速接口设计

参考文献&#xff1a; 一、绪论 1.1 背景 “串行替代并行”&#xff1a; 串行传输使用差分信号传输以传输更长距离&#xff1b; 并行传输因串扰无法长距离传输&#xff1b;并行线路对信号偏斜量的要求&#xff0c;限制了最大的传输速率。 SerDesSerializer Deserializer S…

2024程序员容器化上云之旅-第2集-Ubuntu-WSL2-Windows11版:接近深洞

故事梗概 Java程序员马意浓在互联网公司维护老旧电商后台系统。 渴望学习新技术的他在工作中无缘Docker。 他开始自学Vue3并使用SpringBoot3完成了一个前后端分离的Web应用系统&#xff0c;并打算将其用Docker容器化后用K8s上云。 3 挑选工具 马意浓画好架构图后&#xff…