cpp_10_多重继承_钻石继承_虚继承

1  多重继承

        一个类可以同时从多个基类继承实现代码。

1.1  多重继承的内存布局

        子类对象内部包含多个基类子对象。

        按照继承表的顺序依次被构造,析构的顺序与构造严格相反

        各个基类子对象按照从地址到高地址排列。

// miorder.cpp 多重继承:一个子类有多个基类
#include <iostream>
using namespace std;class A {
public:int m_a;A() { cout << "A()" << endl; }~A() { cout << "~A()" << endl; }
};class B {
public:int m_b;B() { cout << "B()" << endl; }~B() { cout << "~B()" << endl; }
};class C {
public:int m_c;C() { cout << "C()" << endl; }~C() { cout << "~C()" << endl; }
};class D : public A, public B, public C { // 汇聚子类
public:int m_d;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {D d; // |A基类子对象|B基类子对象|C基类子对象|m_d|-->|m_a|m_b|m_c|m_d|cout << "汇聚子类对象d的大小:" << sizeof(d) << endl; // 16D* pd = &d;cout << "整个汇聚子类对象的地址 D* pd: " << pd << endl;cout << "A基类子对象的首地址: " << &d.m_a << endl;cout << "B基类子对象的首地址: " << &d.m_b << endl;cout << "C基类子对象的首地址: " << &d.m_c << endl;cout << "D类自己的成员变量&m_d: " << &d.m_d << endl;return 0;
}

1.2  多重继承的类型转换

        (1)将子类对象的指针,隐式转换为它的某种基类类型指针,编译器会根据各个基类子对象在子类对象中的位置,进行适当的偏移计算,以保证指针的类型与其所指向目标对象的类型一致。

                反之,将任何一个基类类型的指针静态转换为子类类型,编译器,编译器同样会进行适当的偏移计算。

                不建议使用:无论在哪个方向上,重解释类型转换reinterpret_cast都不偏移

       

// convercmp.cpp 多重继承的类型转换
#include <iostream>
using namespace std;class A {
public:int m_a;
};
class B {
public:int m_b;
};
class C {
public:int m_c;
};
class D : public A, public B, public C { // 汇聚子类
public:int m_d;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {D d; // |A基类子对象|B基类子对象|C基类子对象|m_d|-->|m_a|m_b|m_c|m_d|cout << "汇聚子类对象d的大小:" << sizeof(d) << endl; // 16D* pd = &d;cout << "整个汇聚子类对象的地址 D* pd: " << pd << endl;cout << "A基类子对象的首地址: " << &d.m_a << endl;cout << "B基类子对象的首地址: " << &d.m_b << endl;cout << "C基类子对象的首地址: " << &d.m_c << endl;cout << "D类自己的成员变量&m_d: " << &d.m_d << endl;cout << "---------------隐式转换---------------" << endl;A* pa = pd;cout << "D* pd--->A* pa: " << pa << endl;B* pb = pd;cout << "D* pd--->B* pb: " << pb << endl;C* pc = pd;cout << "D* pd--->C* pc: " << pc << endl;cout << "---------------static_cast---------------" << endl;D* p1 = static_cast<D*>(pa);cout << "A* pa--->D* p1: " << p1 << endl;D* p2 = static_cast<D*>(pb);cout << "B* pb--->D* p2: " << p2 << endl;D* p3 = static_cast<D*>(pc);cout << "C* pc--->D* p3: " << p3 << endl;cout << "---------------reinterpret_cast---------------" << endl;pa = reinterpret_cast<A*>(pd);cout << "D* pd--->A* pa: " << pa << endl;pb = reinterpret_cast<B*>(pd);cout << "D* pd--->B* pb: " << pb << endl;pc = reinterpret_cast<C*>(pd);cout << "D* pd--->C* pc: " << pc << endl;return 0;
}

        (2)引用的情况与指针类似,因为引用的本质就是指针。

1.3  多重继承的名字冲突

        如果在子类的多个基类中,存在同名的标识符,那么任何试图通过子类对象,或在子类内部访问该名字的操作,都将引发歧义

        解决办法1:子类隐藏该标识符(因噎废食,不建议);

        解决办法2:通过作用域限定操作符::显示指明所属基类。

// scope.cpp 多重继承的名字冲突问题 -- 作用域限定符
#include <iostream>
using namespace std;class A { // 学生类
public:int m_a;int m_c; // 成绩
};class B { // 老师类
public:int m_b;int m_c; // 工资
};class D : public A, public B { // 助教类
public:int m_d;
//  int m_c; // 在业务上毫无意义,仅仅就是为了将基类的m_c隐藏void foo() {A::m_c = 100; }
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {D d; // |A基类子对象|B基类子对象|m_d| --> |m_a m_c|m_b m_c|m_d|cout << "汇聚子类对象d的大小:" << sizeof(d) << endl; // 20d.B::m_c = 8000; return 0;
}

2  钻石继承

        一个子类继承自多个基类,而这些基类又源自共同的祖先,这样的继承结构称为钻石继承(菱形继承):

        

2.1  钻石继承的问题

        公共基类子对象,在汇聚子类对象中,存在多个实例:

                           

// diamond.cpp 钻石继承
#include <iostream>
using namespace std;
class A { // 公共基类(人类)
public:int m_a; // 年龄
};
class X : public A { // 中间子类(学生类)
public:int m_x;
};
class Y : public A { // 中间子类(老师类)
public:int m_y;
};
class Z : public X, public Y { // 汇聚子类(助教类)
public:int m_z;void foo() {X::m_a = 20; // 歧义}
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {Z z; // |X中间子类子对象|Y中间子类子对象|m_z|-->// |A公共基类子对象 m_x|A公共基类子对象 m_y|m_z|-->|m_a m_x|m_a m_y|m_z|cout << "汇聚子类对象z的大小: " << sizeof(z) << endl; // 20z.Y::m_a = 20; // 歧义return 0;
}

         

3  虚继承

3.1  钻石继承的解决方法

        在继承表中使用virtual关键字。

        虚基类子对象,不在中间子类子对象中,保存在最后。

        虚继承可以保证:

                (1)公共虚基类子对象汇聚子类对象中仅存一份实例 

                

                (2)公共虚基类子对象被多个中间子类子对象共享 

3.2  虚继承实现原理

        汇聚子类对象中的每个中间子类子对象都持有一个指针,通过该指针可以获取 中间子类子对象的首地址 到 公共虚基类子对象 的首地址的 偏移量

                   

// virtualinherit.cpp 虚继承 -- 钻石继承问题的解决法门
// (1) 公共基类(A类)子对象 在 汇聚子类(Z类)对象中 只存在一份
// (2) 公共基类(A类)子对象 要被 多个中间子类(X/Y类)子对象 共享
#include <iostream>
using namespace std;
#pragma pack(1)
class A { // 公共基类(人类)
public:int m_a; // 年龄
};
class X : virtual public A { // 中间子类(学生类)
public:int m_x;void setAge( /* X* this */ int age ) {this->m_a = age; // (1) this (2) X中间子类子对象 (3) 指针1 (4) 偏移量 // (5)this+偏移量 (6)A公共基类子对象首地址 (7)m_a}
};
class Y : virtual public A { // 中间子类(老师类)
public:int m_y;int getAge( /* Y* this */ ) {return this->m_a; // (1) this (2) Y中间子类对象 (3) 指针2 (4) 偏移量 // (5)this+偏移量 (6)A公共基类子对象首地址 (7)m_a}
};
class Z : public X, public Y { // 汇聚子类(助教类)
public:int m_z;void foo() {m_a = 20; }
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {Z z; // |X中间子类子对象|Y中间子类子对象|m_z|A公共基类子对象|-->// |指针1 m_x|指针2 m_y|m_z|m_a|cout << "汇聚子类对象z的大小: " << sizeof(z) << endl; //  32z.setAge( 28 ); // setAge( &z, 28 ); cout << z.getAge() << endl; // getAge( &z );return 0;
}

3.3  虚继承的构造函数

// virtualCons.cpp 虚继承的构造函数
#include <iostream>
using namespace std;
#pragma pack(1)
class A { // 公共虚基类
public:int m_a;
};
// 开关量X=0;
class X : virtual public A { // 中间子类
public:X() {//【*】//【int m_x;】// if( 开关量X==0 ) { // 只有开关量X为0时,X类构造函数才会创建 A虚基类子对象//  【A();】// }}int m_x;
};
// 开关量Y=0;
class Y : virtual public A { // 中间子类
public:Y() {//【*】//【int m_y;】// if( 开关量Y==0 ) { // 只有开关量Y为0时,Y类构造函数才会创建 A虚基类子对象//  【A();】// }}int m_y;
};
class Z : public X, public Y { // 汇聚子类
public:Z( ) {// 开关量X=1,开关量Y=1  (将 开关量置为1 )//【X();】//【Y();】//【int m_z;】//【A();】// 开关量X=0,开关量Y=0  (复位开关量)}int m_z;
};
int main( void ) {X x; // |指针 m_x|A虚基类子对象| --> |指针 m_x|m_a|cout << "x对象的大小: " << sizeof(x) << endl; // 16Y y; // |指针 m_y|A虚基类子对象| --> |指针 m_y|m_a|cout << "y对象的大小: " << sizeof(y) << endl; // 16Z z; // |X中间子类子对象|Y中间子对象|m_z|A公共虚基类子对象| // --> |指针 m_x|指针 m_y|m_z|m_a|cout << "z对象的大小: " << sizeof(z) << endl;return 0;
}

3.4  虚继承特点

        虚基类子对象,不在中间子类子对象中,保存在最后。(通过上述开关量可知)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跑代码相关 初始环境配置

是看了这个视频&#xff1a;深度学习python环境配置_哔哩哔哩_bilibili 总结的个人笔记 这个是从零开始配python环境的比较好的经验教程&#xff1a; 深度学习python的配置&#xff08;Windows&#xff09; - m1racle - 博客园 (cnblogs.com) 然后关于CUDA和cuDNN&#xff…

武汉灰京文化:跨平台游戏的崛起,破壁无界,畅游全场!

随着科技的飞速发展&#xff0c;游戏平台之间的界限逐渐模糊&#xff0c;许多优秀的游戏已经不再只局限于单一的游戏设备平台。无论你身处何处&#xff0c;只要有网络连接&#xff0c;你就可以随时随地畅玩自己喜欢的游戏&#xff0c;而无需更换设备或重新安装&#xff0c;这无…

UniRepLKNet实战:使用UniRepLKNet实现图像分类任务(一)

文章目录 摘要安装包安装timm 数据增强Cutout和MixupEMA项目结构计算mean和std生成数据集一些问题 摘要 大核卷积神经网络&#xff08;ConvNets&#xff09;近年来受到广泛关注&#xff0c;但仍存在两个关键问题需要进一步研究。首先&#xff0c;目前的大型卷积神经网络架构大…

docker 安装redis (亲测有效)

目录 1 安装 1 安装 1 将redis 的 tar 包 上传到服务器 上传之后tar 包&#xff0c;将他变成镜像 输入docker images,发现目前是没有镜像的&#xff0c;现在将tar 包变成镜像 docker load -i redis.tar以上就将tar 包变成镜像了 现在在宿主机找一个地方&#xff0c;存放数据…

Pytorch的GPU版本安装,在安装anaconda的前提下安装pytorch

本文基于conda安装GPU版本的PyTorch 文章目录 一、CUDA1.下载CUDA [点击下载](https://developer.nvidia.com/cuda-toolkit-archive)2.cuDNN [下载](https://developer.nvidia.com/login) 二、pytorch下载1.下载 [pytorch](https://pytorch.org/)2.查看cuda版本3.找到对应的版…

前缀和算法模板

一维前缀和 算法用途&#xff1a;快速求出数组中某一连续区间的和 一维前缀和算法模板 1、预处理出一个 dp 数组 要求原数组存储在 n 1 的空间大小中&#xff0c;其中后 n 个空间存数据。 dp数组&#xff0c;数组开 n 1个空间&#xff0c;dp[i] 表示 [ 1, i ] 区间内所有…

PyQt5零基础入门(二)——QLabel控件

前言 QLabel控件可以视为是一个标签项&#xff0c;具有显示文本、图像的作用。在本篇文章中将介绍QLabel控件的常见用法。 例子 显示文本 import sys from PyQt5.QtWidgets import *if __name__ "__main__":app QApplication([])label QLabel(Hello world!)la…

系统存储架构升级分享

一、业务背景 系统业务功能&#xff1a;系统内部进行数据处理及整合, 对外部系统提供结果数据的初始化(写)及查询数据结果服务。 系统网络架构: • 部署架构对切量上线的影响 - 内部管理系统上线对其他系统的读业务无影响 •分布式缓存可进行单独扩容, 与存储及查询功能升级…

String#intern

1.intern方法 intern()方法可以在运行期间向字符串中动态加入字符串实例的方式&#xff0c;它的功能很简单,总结起来就一句话 可以在运行时向字符串池中添加字符串常量 添加的原则是&#xff0c;如果常量池中存在当前字符串&#xff0c;则直接返回常量池中它的引用&#xff1b…

NPS配置https访问web管理页面

因为NPS默认也支持http的访问&#xff0c;所以在部署完后就一直没在意这个事情。 因为服务器是暴露在公网内的&#xff0c;所以还是要安全一点才行。不然一旦远控的机器被破解了就很危险了 一、使用nginx反向代理访问 1、首先在nps的配置文件里关闭使用https选项&#xff0c;…

m1 + swoole(hyperf) + yasd + phpstorm 安装和debug

参考文档 Mac M1安装报错 checking for boost... configure: error: lib boost not found. Try: install boost library Issue #89 swoole/yasd GitHub 1.安装boost库 brew install boostbrew link boost 2.下载yasd git clone https://github.com/swoole/yasd.git 3.编…

@RequestParam

在我们写接口的时候&#xff0c;经常会用到这个注解来标记参数&#xff0c;通过这个注解我们可以把请求的url中的参数名和值映射到被标记的参数上。 比如下方&#xff0c;这个接口是通过传入的参数来查询相关信息的 我们定义这样一个接口&#xff0c;设置了8个参数&#xff0c;…

银联商务:Apache Doris 赋能“科技银商”,助力金融机构挖掘增长新机遇

本文导读&#xff1a; 在长期服务广大规模商户的过程中&#xff0c;银联商务已沉淀了庞大、真实、优质的数据资产数据&#xff0c;这些数据不仅是银联商务开启新增长曲线的基础&#xff0c;更是进一步服务好商户的关键支撑。为更好提供数据服务&#xff0c;银联商务实现了从 H…

【Python】编程练习的解密与实战(一)

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《Python | 编程解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1fa90;1. 初识Python &a…

【Flutter 开发实战】Dart 基础篇:最基本的语法内容

在深入了解 Dart 这门编程语言之前&#xff0c;我们需要了解一些关于 Dart 的最基本的知识&#xff0c;像是常量、变量、函数等等&#xff0c;这样才能够让我们的开发效率更上一层楼。在本节&#xff0c;我们将探讨一些基础语法&#xff0c;包括入口方法 main、变量、常量以及命…

中国农业熟制区划数据, Shp格式,高清大图可获取

数据基本信息. 数据名称: 中国农业熟制区划数据 数据格式: Shp 数据时间: 未知 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据 示例数据&#xff1a; 序号区域名称1川鄂湘黔低高原山地水田旱地二熟兼一熟区2大小兴安岭山麓岗地凉温作物…

STM32蓝牙小车、红外循迹小车、超声波避障小车项目设计

一、前言 本文旨在分享我学习STM32的过程中&#xff0c;为了强化学习成果&#xff0c;试着制作一些实训项目。最开始做的就是STM32蓝牙小车、STM32红外循迹小车、STM32超声波避障小车。 相信看完本文的你&#xff0c;一定可以亲手制作一辆属于自己的智能小车&#xff01; 注&am…

C语言入门教程,C语言学习教程(第三部分:C语言变量和数据类型)二

十、在C语言中使用英文字符 前面我们多次提到了字符串&#xff0c;字符串是多个字符的集合&#xff0c;它们由" "包围&#xff0c;例如"http://c.biancheng.net"、"C语言中文网"。字符串中的字符在内存中按照次序、紧挨着排列&#xff0c;整个字…

STM32F103RCT6使用数据手册及应用示例程序分享

STM32F103RCT6是意法半导体&#xff08;STMicroelectronics&#xff09;推出的一款Cortex-M3内核的高性能微控制器。它具有丰富的外设功能和强大的处理能力&#xff0c;适用于多种应用场景。 要进行手册数据分析&#xff0c;首先需要下载并查阅STM32F103RCT6的技术参考手册。可…

【已解决】安装fasttext、py2neo失败

安装fasttext 1.官方方法&#xff08;不好使&#xff09; pyfasttext PyPI pip install cysignals pip install pyfasttext报错&#xff1a; Building wheels for collected packages: cysignalsBuilding wheel for cysignals (PEP 517) ... errorERROR: Command errored …