C++继承——多继承导致的菱形继承

news2024/12/30 3:34:24

目录

单继承: 

多继承:

菱形继承:菱形继承是多继承的一种特殊情况。

三.菱形继承的两种解决方式区别:

3.1采用作用域解决的菱形继承:

检测器运行图: 

反汇编运行图: 

3.1菱形虚继承:

检测器运行图: 

反汇编的运行图: 


继承分为单继承和多继承。

单继承: 

class A{
public:
	A(int a) {}
protected:
	int _a;
};

class B :public A {
public:
	B(int a,int b) 
	:A(a) 
	,_b(b){ }
protected:
	int _b;
};

class C :public B {
public:
	C(int a,int b,int c)
		:B(a,b)
		,_c(c) {}
protected:
	int _c;
};

多继承:

class A {
public:
	A(int a = 10)
		:_a(a) {
		cout << "A()的构造函数" << endl;
	}
protected:
	int _a;
};

class B  {
public:
	B( int b=20)
		: _b(b) {
		cout << "B()的构造函数" << endl;
	}
protected:
	int _b;
};

//子类C同时继承了父类A,父类B

class C :public A,public B {
public:
	C(int a=9, int b=8, int c=7)
		:B(b)
		,A(a)
		, _c(c) {
		cout << "C()的构造函数" << endl;
	}
	void Show() {
		cout << "_a的值为:"<<_a << endl;
		cout << "_b的值为:" << _b << endl;
		cout << "_c的值为:" << _c << endl;
	}
protected:
	int _c;
};


int main() {
	C c1(1,2,3);
	c1.Show();
	return 0;
}

        多继承大大增强了代码的复用性,可以让一个简单的类去使用多个父类的成员,减少了代码的冗余性,一个指向多个基类的子类可以调用多个基类的不同方法,调用多个基类的成员变量。

菱形继承:菱形继承是多继承的一种特殊情况。

        A类和B类继承了S类,而C类又同时继承了A类和B类,这种多继承会导致二义性和数据冗余!这是C++设计多继承所导致的一个大坑!!! 

 举个例子:

class Person {
public:
	void Sleep() {
		cout << "睡觉技能" << endl;
	}
	void Eat() {
		cout << "吃饭技能" << endl;
	}
};

//子类1:
class Author:public Person {
public:
	void Write_book() {
		cout << "写作技能" << endl;
	}
};

//子类2:
class Programmer :public Person {
public:
	void Programming() {
		cout << "编程技能" << endl;
	}
};

//孙子类
class Pro_Writer :public Author, public Programmer {
public:
	void skill() {
		cout << "既能编程又能写文章" << endl;
	}
};

案例测试: 

int main() {
	Pro_Writer pw;
	pw.Write_book();	//pw调用父类Author的方法
	pw.Programming();	//pw调用父类Programmer的方法

    pw.Sleep();
	pw.Eat();
	
	return 0;
}

        在上方案例测试中,孙子类创建了一个对象,该对象调用了从父类Author继承的Write_book函数和Programmer继承的Programming函数,都挺好,增加了代码复用性;紧接着,该对象又调用了从父类继承得来的Sleep和Eat函数,这时就出现了分歧,因为这两个成员函数是Author类和Programmer类以及Person类都有的成员函数,编译器压根不知道程序员到底想调用哪个!!!

 于是菱形继承的弊端就出来了,二义性体现的淋漓尽致。

有人会提到:在调用前指定类作用域不就行了吗?那样编译器就知道该调哪个类的Sleep函数了。

这种方法可以解决菱形继承带来的弊端

//解决方法1:——加作用域
	pw.Author::Sleep();
	pw.Programmer::Eat();
	//这样,编译器就能精准的调用


        其实还有一种更为官方的解决方式,C++官方为了解决了菱形继承带来的弊端,发明了virtual关键字,在菱形继承中为中间两个类的声明上加上virtual关键字,便可以解决二义性!

解决形式:

                                               classA{ };

class B: virtual public A{ };                            class C: virtual public A{ };

                                    class D: public B,public C{ };

class Person {
public:
	void Sleep() {
		cout << "睡觉技能" << endl;
	}
	void Eat() {
		cout << "吃饭技能" << endl;
	}
};

//子类
//虚继承virtual
class Author : virtual public Person {
public:
	void Write_book() {
		cout << "写作技能" << endl;
	}
};

//虚继承
class Programmer :virtual public Person {
public:
	void Programming() {
		cout << "编程技能" << endl;
	}
};

class Pro_Writer :public Author, public Programmer {
	void skill() {
		cout << "既能编程又能写文章" << endl;
	}
};

int main() {
	Pro_Writer pw;
	pw.Write_book();	//pw调用父类Author的方法
	pw.Programming();	//pw调用父类Programmer的方法

	pw.Sleep();
	pw.Eat();
	return 0;
}

代码解析:

        在使用virtual前,两个子类Author和Programmer的Sleep,Eat函数都各家是各家的,各有各的函数地址,只不过名字相同。在不指定作用域前,编译器就是靠名字去找函数。

        对中间两个子类使用虚继承virtual后,Author和Programmer两个子类的Sleep,Eat函数的地址是一样的了,即使不用作用域,编译器会将这两个类的这俩函数看成同一份函数,就好比公共资源一般,只要是Author和Programmer两个子类从Person继承过来的成员变量或者是成员函数,都是一体的,不分你我。

运行结果: 

三.菱形继承的两种解决方式区别:

3.1采用作用域解决的菱形继承:

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=5;    //指代不明确——二义
	d.A::_a = 1;
	d._b = 2;
	d._c = 3;
	d._d = 4;
	d.B::_a = 5;
	d.C::_a = 6;
	return 0;
}

检测器运行图: 

反汇编运行图: 

        从上图就可知:在指定作用域去修改_a变量的寄存器所存地址对于这三个类并不是完全相同的。


3.1菱形虚继承:

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

	int main() {
		D d;
		d._a = 1;
		d._b = 2;
		d._c = 3;
		d._d = 4;
		d._a = 5;
		d._a = 6;
    return 0;
}

检测器运行图: 

在检查器运行图中,基类A的_a变量独立显示出来了。 

反汇编的运行图: 

        B,C类作为子类,继承了A类的成员变量_a,D作为子类,又同时继承了B,C类。通过调试会发现,对象d在给_a赋值为1时,D类中的_a值为1,并且B类中的_a,C类中的_a也赋值为1
        这就是因为有virtual,使得编译器对3个不同类中的_a成员变量,关联成一体,一荣俱荣,一损俱损,也就不再会出现二义性的问题了!!!

        总结:一般不建议设计出多继承,这样可能会在不知不觉间设计出菱形继承,设计菱形继承,就相当于给自己挖了一个坑,在复杂度及性能上都有问题。 

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

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

相关文章

私募证券基金动态-23年7月报

成交量&#xff1a;7月日均8,481.84亿元 2023年7月A股两市日均成交8,481.84亿元&#xff0c;环比下降11.78%、同比下降15.77%。7月整体21个交易日&#xff0c;仅有月初第1个交易日单日成交金额过万亿&#xff0c;且成交量起伏较大&#xff0c;单日成交金额最低仅有6610.11亿元…

深度学习(32)——CycleGAN(1)

深度学习&#xff08;32&#xff09;——CycleGAN&#xff08;1&#xff09; 文章目录 深度学习&#xff08;32&#xff09;——CycleGAN&#xff08;1&#xff09;1. GAN原理2. CycleGAN&#xff08;1&#xff09;原理&#xff08;2&#xff09;核心思想&#xff08;3&#xf…

双环抱式“星环“座舱设计:比亚迪仰望U8内饰曝光,搭载骁龙8+车机

根据8月3日的消息&#xff0c;比亚迪车机先前使用的高通骁龙625芯片在网友中引发了一些批评&#xff0c;不过随着比亚迪将车机升级为骁龙665、骁龙690/695&#xff0c;这个问题得到了改善。 与此同时&#xff0c;大多数主流车企还在继续使用高通8155芯片&#xff08;相当于骁龙…

【Kubernetes】

目录 一、Kubernetes 概述1、K8S 是什么&#xff1f;2、为什么要用 K8S?3、Kubernetes 集群架构与组件 二、核心组件1、Master 组件2、Node 组件3、K8S创建Pod的工作流程&#xff1f;&#xff08;重点&#xff09;4、K8S资源对象&#xff08;重点&#xff09;5、Kubernetes 核…

基于Dockerfile构建镜像应用

目录 一、镜像概述 二、镜像构建方式 三、镜像构建案例 3.1、基于已有容器创建镜像 3.2、基于本地模板创建镜像 3.3、基于Dockerfile构建镜像 3.3.1、Docker 镜像结构 3.3.2、Dockerfile介绍 3.3.3、Dockerfile详解 3.3.4、Dockerfile构建SSHD镜像 3.3.5、Dockerfile…

基于Vue3的数据可视化低代码平台

经过技术调整与迁移&#xff0c;JNPF快速开发平台升级为Vue3.0技术。 Vue3.0作为最新版本的Vue.js框架&#xff0c;拥有更强大的性能和更丰富的功能&#xff0c;为低代码开发平台注入了全新的活力。而JNPF快速开发平台作为其应用的典范之一&#xff0c;利用Vue3.0的优势&#x…

VUE框架、UNIAPP框架:vue2转vue3全面细节总结(1)vue框架:路由模式、路由跳转;uniapp框架:存储数据写法、引入数据写法;

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人_python人工智能视觉&#xff08;opencv&#xff09;从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了&#xff1a; https://blog.csdn.net/lbcy…

list删除重复元素几种思路

文章目录 list删除重复元素几种思路hashsetStream流删除所有重复元素 list删除重复元素几种思路 hashset List<String> list2 new ArrayList<>();list2.add("a");list2.add("b");list2.add("a");Set<String> set new HashS…

Unity | 编辑器运行时固定启动场景

1 简介 在多场景游戏的开发过程中&#xff0c;通常需要按照特定的顺序启动场景&#xff0c;以完成一些初始化工作。因此当我们想要在编辑器中运行某个中间场景时&#xff0c;必须先切换到第一个场景。这样的操作不仅繁琐&#xff0c;而且影响开发效率。利用 RuntimeInitialize…

google谷歌gmail邮箱账号注册手机号无法进行验证怎么办?此电话号码无法用于进行验证 或 此电话号码验证次数太多

谷歌gmail邮箱账号注册手机号无法进行验证怎么办? 使用手机号码注册谷歌gmail邮箱账号时会遇到&#xff1a;此电话号码无法用于进行验证 或 此电话号码验证次数太多。造成注册google谷歌gmail邮箱账号受阻&#xff0c;无法正常完成注册。 谷歌Gmail邮箱账号正确的注册方法与教…

MySQL数据库概述(一)

夕阳留恋的不是黄昏&#xff0c;而是朝阳 Mysql 新系列参考 尚硅谷 宋红康老师的教材文档&#xff0c;后续不再进行说明。 一. 为什么要使用数据库 主要是为了进行持久化。 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#…

Linux操作系统(三):详解Linux及2023年gcc/g++ 多版本间切换

目录 1. Linux操作系统之Red hat与Centos2. 多个gcc/glibc版本的共存及指定gcc版本的编译2.1 如何确定cuda版本和gcc版本不适配2.2 cuda降级&#xff08;不推荐&#xff09;2.3 gcc多版本管理2.4 直接换一台服务器 1. Linux操作系统之Red hat与Centos 基于目前手头的Linux操作…

二、搜索与图论6:Dijkstra 模板题+算法模板(Dijkstra求最短路 I, Dijkstra求最短路 II,1003 Emergency)

文章目录 算法模板Dijkstra题目代码模板朴素dijkstra算法堆优化版dijkstra 树与图的存储(1) 邻接矩阵&#xff1a;(2) 邻接表&#xff1a;关于e[],ne[],h[]的理解 关于堆的原理与操作 模板题Dijkstra求最短路 I原题链接题目思路题解 Dijkstra求最短路 II原题链接题目思路题解 1…

cpolar内网穿透可应用于哪些场景?

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于恒川的日常汇报系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏C语言初…

RGB颜色值与十六进制颜色码对照表(超详细)

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

VGG卷积神经网络-笔记

VGG卷积神经网络-笔记 VGG是当前最流行的CNN模型之一&#xff0c; 2014年由Simonyan和Zisserman提出&#xff0c; 其命名来源于论文作者所在的实验室Visual Geometry Group。 测试结果为&#xff1a; 通过运行结果可以发现&#xff0c;在眼疾筛查数据集iChallenge-PM上使用VGG…

maven发布到中央仓库

创建账号 https://issues.sonatype.org 【第二步】登录申请新项目 右上角点击Create&#xff0c;Project选择第一项&#xff0c;有的时候带不出来第二个New Project&#xff0c;可以再选一次Project的选项。

warning Replace `‘vue‘` with `“vue“;`

warning Replace vue with "vue"; 如果报以上错误 不要怀疑直接找 .eslintrc 元凶就是他 方法一 在配置关闭eslint语法检测 在vite.config.ts文件中 方法二 适用于项目中不得不依赖 eslint 进行配置结尾换行符&#xff0c;那么就会直接在开发环境中进行验证。…

面向对象程序三大特性一:多态(超详细)

目录 1.重写 1.1基本语法规则 1.2规则深化 1.3重写与重载的区别 2.向上转型 2.1简单介绍 2.3向上转型的作用 3.向下转型 3.1介绍 3.2instanceof 基本介绍 4.多态 4.1多态实现条件 4.2避免在构造方法中调用重写的方法 1.重写 重写 (override) &#xff1a;也称为覆…

数据库导出Excel格式的表结构

数据库导出Excel格式的表结构 你是否遇到到导出数据库里面的表结构&#xff0c;包含字段名称、类型、长度、小数、默认值、字段描述之类的需求&#xff1b;当我们去navcat里面找时发现没有&#xff0c;因为navcat没有提供这一功能&#xff0c;他只可以导出表结构的sql&#xff…