C++继承——多继承问题

news2024/9/22 19:28:29

目录

单继承: 

多继承:

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

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

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/818763.html

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

相关文章

webstorm配置less转译

Program中路径如果识别不到 项目文件\node_modules.bin\lessc

如何优雅的显示404页面

源码&#xff1a;mumangguo/404-notfound - 码云 - 开源中国https://gitee.com/mumangguo/404-notfound 1.孤独型404页面 2.酷炫效果404页面 3.太空404页面 4.404寻亲页面&#xff08;公益&#xff09; 每一次刷新都是一个公益捐赠活动&#xff01; 以上就是笔者要分享的4个4…

【H5移动端】常用的移动端方案合集-键盘呼起、全面屏适配、图片大小显示、300ms点击延迟、首屏优化(不定期补充~)

文章目录 前言键盘呼起问题靠近底部的输入项被键盘遮挡底部按钮被顶上去 全面屏适配图片大小显示问题解决300ms延迟首屏优化 前言 这篇文章总结了我在工作中做H5遇到的一些问题&#xff0c;包括我是怎么解决的。可能不是当下的最优解&#xff0c;但是能保证解决问题。 单位适…

【hive】Install hive using mysql as hive metadata service

文章目录 一. Requirements二. Installing Hive from a Stable Release三. Running Hive四. Running Hive CLI五.Running HiveServer2 and Beeline1. 下载安装mysql2. 下载mysql驱动3. 配置hive-site.xml4. 初始化元数据库5. 通过beeline进行连接 一. Requirements Users are s…

简易博客系统自动化测试

&#x1f349;作者 Autumn60 &#x1f3c4;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f472;微语 &#xff1a;只有镜子和钱包&#xff0c;可以告诉你生活中&#xff0c;大部分的为什么和凭什么 文章目录 目录 文章目录 概述: 测试方法&am…

自动化测试和手动测试相比,哪个更具优势?

在软件测试行业中&#xff0c;争议最大的话题是“更好的是手动测试还是自动化测试”。尽管自动化测试最常谈论流行语&#xff0c;并且正在慢慢主导测试领域&#xff0c;手动测试的必要性不可忽视。 在本文中&#xff0c;将探讨手动测试和自动化测试之间的更深差异。 什么是手动…

javascript实现几何粒子星空连线背景效果

javascript实现几何粒子星空连线背景效果 <html><head><meta charset"UTF-8"><title>几何星空连线背景</title><script src"./ParticleBackground.js"></script> </head><body><canvas id"…

【计算机视觉】BLIP:统一理解和生成的自举多模态模型

文章目录 一、导读二、背景和动机三、方法3.1 模型架构3.2 预训练目标3.3 BLIP 高效率利用噪声网络数据的方法&#xff1a;CapFilt 四、实验4.1 实验结果4.2 各个下游任务 BLIP 与其他 VLP 模型的对比 一、导读 BLIP 是一种多模态 Transformer 模型&#xff0c;主要针对以往的…

如何选择微信客户管理系统?

为何要给客户打上标签&#xff1f; 主要为企业搭建一个完善的客户体系&#xff0c;将客户资源整合&#xff0c;分层管理并进行针对性营销推广&#xff0c;以实现精准获客转化&#xff0c;简单来说就是更好的分类管理。 客户标签不应该只是作为的客户登记资料&#xff0c;后续每…

一起来探索用ai绘画二次元描述词绘制出来图画吧

在二次元的世界中&#xff0c;画笔是创作的灵魂&#xff0c;绘画作品是艺术家灵魂的抒发。而如今&#xff0c;随着科技的不断进步&#xff0c;我们迎来了一款令人兴奋的技术——ai绘画。这项软件可以帮助我们创造出色彩斑斓、令人惊叹的二次元作品。让你无需具备专业绘画技巧&a…

为什么说国内数字孪生平台gis架构采用Cesium是不错的选择?

Gis作为数字孪生平台开发中重要的一环对数字孪生平台是否好用是一个重要的判定方式&#xff0c;国内数字孪生软件在融合GIS系统方面采取了多种方式&#xff0c;例如Unity或Unreal Engine等游戏引擎&#xff0c;以增强数字孪生的空间感知和空间分析能力&#xff0c;提供更全面、…

银河麒麟V10系统忘记密码解决

1、正常开机等待系统进入GRUB界面&#xff0c;按下e键&#xff0c;如下图&#xff1a; 2、进入编辑文件&#xff1a; 3、 修改ro为rw 然后再行尾添加空格输入&#xff1a;consoletty init/bin/bash (或 single init/bin/bash) 输入完成按&#xff1a;ctrlx或F10进入修改密码阶段…

千元内合板和单板吉他怎么选?SAGA萨伽SF600和VEAZEN费森CLR300怎么样?哪一款更适合初学者入门使用!【吉他评测】

对于预算不多的朋友&#xff0c;在选购前翻阅查询很多资料&#xff0c;吉他材质、桶型和尺寸等等疑问&#xff0c;不知道怎么选&#xff0c;无从下手&#xff0c;还容易遇到烧火棍&#xff0c;在这里介绍这两款VEAZEN费森CLR300&#xff08;单板&#xff09;和SAGA萨伽SF600&am…

<Doc>Windows常见的doc命令

一&#xff1a;管理员身份运行cmd命令&#xff1a; 方式一&#xff1a;搜索框输入cmd&#xff0c;回车&#xff0c;点击&#xff1a;以管理员身份运行 出现如图所示&#xff1a; 方式二&#xff1a;快捷键运行方式&#xff1a; 1.按winr&#xff0c;在运行窗口中输入cmd。 …

视频过大如何压缩变小?文件压缩技巧分享

如何压缩视频是许多视频编辑者、视频上传者经常遇到的问题&#xff0c;如果你也遇到了这个问题&#xff0c;不用担心&#xff0c;下面将就给大家分享几个视频压缩方法&#xff0c;可以帮助大家轻松地压缩视频&#xff0c;同时保持视频的高清晰度和音频质量。 一、嗨格式压缩大师…

HTML+CSS前端 动态响应用户登录界面

day2 知道了动态响应设计的概念&#xff0c;在原先登录界面的基础上进行升级 动态响应 由于前端页面需要在不同大小和分辨率的屏幕上显示&#xff0c;所以需要它具有动态适应的特性。 常用的方式是在 css 文件中用 media 动态查询&#xff0c;同时使用 flex 弹性布局。 例如&a…

C++深度探索

1.变量的实现机制 在声明一个变量时&#xff0c;需要指定它的数据类型和变量名&#xff0c;在源代码中它们都用文字来表示&#xff0c;这种文字形式便于人们阅读&#xff0c;计算机CPU无法直接识别。在C源程序中&#xff0c;之所以要使用变量名&#xff0c;是为了把不同的变量…

电子文件管理系统的核心功能与优势解析

电子文件管理系统是一种通过数字化技术来管理、存储和检索文件的系统。它的核心功能主要包括文件存储、文件检索、权限管理和版本控制等&#xff0c;这些功能给用户带来了许多优势。 文件存储是电子文件管理系统的基础功能之一。该系统可以将各种类型的文件以电子形式进行存储…

unity摄像机跟随玩家

1.下载摄像机包 2.创建摄像机 3.拖拽玩家

虹科案例|如何分析设备故障时间和次数,打破生产瓶颈?

虹科设备绩效管理系统 保障生产设备的稳定性和可靠性 生产设备的稳定性和可靠性是保证企业正常生产的重要条件之一&#xff0c;设备故障的频发严重影响企业的正常生产&#xff0c;那么如何分析设备故障时间和次数&#xff0c;查找设备故障原因&#xff0c;协助企业打破生产瓶…