【C++历险记】面向对象|菱形继承及菱形虚拟继承

这篇具有很好参考价值的文章主要介绍了【C++历险记】面向对象|菱形继承及菱形虚拟继承。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

个人主页:兜里有颗棉花糖💪
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【C++之路】💌
本专栏旨在记录C++的学习路线,望对大家有所帮助🙇‍
希望我们一起努力、成长,共同进步。🍓
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

一、多继承以及菱形继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

比如:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

比如:【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

菱形继承:菱形继承是多继承的一种特殊情况,指一个派生类直接或间接地从两个或者更多个基类继承成员,而这些基类又直接或间接地继承自同一个基类。

比如:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

下面是两种简单的菱形继承的模型:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

二、多继承引发的问题

C++继承体系中的多继承虽然给我们提供了代码的灵活性和重用性,但是也会引发一些问题:多继承会引发菱形继承问题,而菱形继承问题又会引发菱形虚拟继承问题。

下面来看菱形继承的问题:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象
上面的对象成员模型构造中,可以看出菱形继承有数据冗余二义性的问题。在Assistant的对象中Person成员会有两份(即_age有两份)。

  • 数据冗余问题(本质就是浪费空间):存在数据重复的问题,比如Person中的_age要存储两份
  • 二义性问题访问不明确,如下图。
    【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

多继承二义性问题的解决方式

C++是如何解决多继承带来的二义性问题呢?

方式一:作用域解析运算符

我们可以通过作用域解析运算符,即::来解决多继承中的二义性问题。使用作用域解析运算符来明确指定调用哪个基类的成员函数或变量。
请看:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

方式二:虚拟继承

虚拟继承:用于解决菱形继承或多继承中的二义性问题的一种机制。通过使用virtual关键字,在继承链中只创建一个共同基类的实例,从而避免了二义性。

请看:

class Person
{
public:
	string _name; // 姓名
	int _age;
};
class Student : virtual public Person
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person
	
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test1()
{
	Assistant as;
	as.Student::_age = 18;
	as.Teacher::_age = 21;
	as._age = 24;
}

解释:

首先,有一个基类 Person,它包含了姓名 _name 和年龄 _age 两个成员变量。

接下来有两个派生类 Student 和 Teacher,它们都以虚拟继承的方式继承自基类 Person。这样做是为了避免后续的Assistant 类在同时继承Student 和 Teacher 时,包含了两个相同的 Person 实例导致的二义性问题。

最后,有一个派生类 Assistant,它同时继承自 Student 和 Teacher 类。由于 Student 和 Teacher都是以虚拟继承的方式继承自 Person,在Assistant 类中就只会有一个共同的 Person 实例。

【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

调试结果如下:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

三、虚拟继承解决数据冗余和二义性的原理

我们已经知道:虚拟继承是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.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

运行调试结果如下:

监视窗口
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

内存窗口
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

可以看到上述代码中存在数据冗余的问题类D继承了类B和类C,与此同时类B和类C都继承了类A,所以可以看到在类D中有两个继承自类A的子对象,分别来自类B和类C。因此,在类D中存在数据冗余,同一个成员变量_a在类D的内存布局中会出现两次,一次来自类B的继承,一次来自类C的继承。这是因为默认情况下,多次继承同一个基类会导致该基类的成员在派生类中有多份副本

下面来看虚拟继承是如何解决上述问题的,请看:

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.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	d._a = 0;
	return 0;
}

下面是上述代码虚拟继承的调试内存窗口:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

示例二(仅仅添加了对象D d2;):

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

现在再来看一下内存窗口:
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象
【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象

示例三(再来看一个对象模型):

int main()
{
	B b;
	b._a = 1;
	b._b = 2;
	return 0;
}

【C++历险记】面向对象|菱形继承及菱形虚拟继承,C++之路,c++,面向对象
示例四:

int main()
{
	D d;
	d._a = 1;

	B b;
	b._a = 2;
	b._b = 3;

	B* ptr = &b;
	ptr->_a++;

	ptr = &d;
	ptr->_a++;
	return 0;
}

指针 ptr 指向对象 b 或对象 d 时,无论 ptr 指向的是哪个对象,当使用 ptr->_a 访问类A的成员时,编译器都会使用存储在类D对象中的偏移量来调整指针,以便正确地访问虚基类A的成员变量 _a

好了,本文就到这里啦,再见啦友友们!!!文章来源地址https://www.toymoban.com/news/detail-692204.html

到了这里,关于【C++历险记】面向对象|菱形继承及菱形虚拟继承的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数通王国历险记之地址分析协议(ARP)

    目录 前言 一,什么是地址解析协议(ARP)  二,封装和解封装 三,为什么需要地址解析协议(ARP)  四,ARP的验证实验 4.1,先创建拓扑结构: 4.2,配置PC1和路由器 4.3,我们先开始进行抓包 4.4,抓包结果 4.5,总结ARP协议 五,ARP的工作原理 5.1  5.2 5.3 5.4 总结 随着华为公司

    2024年02月13日
    浏览(81)
  • 图灵日记之java奇妙历险记--异常&&包装类&&泛型

    在java中,将程序执行过程中发生的不正常行为称为异常 异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为: 编译时异常 在程序编译期间发生的异常,称为编译时异常,也称为受检查异常 运行时异常 在程序执行期间发生的异常,.称为运行时异常

    2024年01月23日
    浏览(29)
  • 图灵日记之java奇妙历险记--抽象类和接口

    在面向对象的概念中,所有对象都是通过类来描述的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息描绘一个具体的对象,这样的类就是抽象类 使用abstract修饰的方法称为抽象方法 使用abstract修饰的类称为抽象类 抽象类是不可以进行实例化的

    2024年02月03日
    浏览(33)
  • Hadoop历险记(七 Hive配置和启动hiveservice2服务)

    之前讲了hive的元数据,它存储着hiveSQL映射和hdfs具体文件数据的对应关系。 但是元数据存在单独的mysql中 直接将它暴露给外部是不安全的,所以hive特别增加了一个元数据服务。 它作为元数据和外部客户端之间的一个中间人。 metastore有三种服务配置模式 同时也可以说这是h

    2024年02月03日
    浏览(34)
  • 林浩然与杨凌芸的Java时光魔法:格式化历险记

    林浩然与杨凌芸的Java时光魔法:格式化历险记 The Java Time Odyssey of Lin Haoran and Yang Lingyun: A Formatting Adventure 在编程世界的一隅,有一个名叫林浩然的程序员。他是个Java大侠,对代码世界的法则了如指掌,尤其擅长驾驭时间这个变幻莫测的维度。而他的生活伴侣、同样是码界巾

    2024年02月20日
    浏览(26)
  • 图灵日记之java奇妙历险记--数据类型与变量&&运算符

    看下面这个栗子,宝子们 输出的\\\"cheer up!!!\\\"就是字面变量 字面常量的值在程序的整个生命周期中是不变的 字面常量的分类: 字符串常量 整形常量 浮点数常量 字符常量 布尔常量:只有两种true和false 空常量 注意:字符串,整形,浮点型,字符型以及布尔型,在java中都被叫作数据类型

    2024年02月04日
    浏览(34)
  • 振南技术干货集:znFAT 硬刚日本的 FATFS 历险记(9)

    注解目录 1、znFAT 的起源 1.1 源于论坛 (那是一个论坛文化兴盛的年代。网友 DIY SDMP3 播放器激起了我的兴趣。) 1.2 硬盘 MP3 推了我一把 (“坤哥”的硬盘 MP3 播放器,让我深陷 FAT 文件系统不能自拔。) 1.3 我的导师-- 顾国昌教授 (哈军工时期的老教授,德高望重的人生导师

    2024年02月03日
    浏览(34)
  • 前台测试转后台优化历险记,应届生薪资8K逆袭,从此扶摇直上九万里!

    优橙教育每一期都会有不少从前台测试转到后台的小伙伴应邀而来,其实每个人的经历都是大致相同的,这时候肯定会有很多小伙伴问,为什么出来花钱出来参加培训而不是在项目上转呢? 或许是因为在项目上摸爬滚打太久了,吃不下领导画的大饼了;也或许是因为确实整天

    2024年02月13日
    浏览(40)
  • 数通王国历险记之TCP协议的三次握手和四次挥手

    目录 前言  一、TCP我们称之为可靠的传输层协议,为什么称它为可靠呢? 二、TCP的建立——三次握手 1,提前知道TCP协议报文中都有些啥? 2.第一次握手 总的来说:就是PC1向PC2发出一个同步报文说,我想和你建立连接 3,第二次握手 总的来说:就是PC2同意和PC1建立连接,同时确

    2024年02月11日
    浏览(30)
  • K8S历险记-从零开始kubeadm单机安装部署k8s保姆级教程

    1.查看系统版本信息以及修改配置信息 1.1 查看cpu信息 k8s安装至少需要2核2G的环境,否则会安装失败 1.2 安装k8s时,临时关闭swap ,如果不关闭在执行kubeadm部分命令会报错 1.3 安装k8s时,可以临时关闭selinux,减少额外配置 1.4 关闭防火墙 1.5 设置网桥参数 1.6 修改hosts文件

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包