C++继承总结(下)——菱形继承

一.什么是菱形继承

菱形继承是多继承的一种特殊情况,一个类有多个父类,这些父类又有相同的父类或者祖先类,那么该类就会有多份重复的成员,从而造成调用二义性和数据冗余。

class Person
{public:Person(){cout << "Person构造" << endl;}
public:int _name = 0;int _age = 0;
};class Student :  public Person
{public:Student(){cout << "Student构造" << endl;}int _stuid = 0;
};class Teacher :  public Person
{
public:Teacher(){cout << "Teacher构造" << endl;}int _jobid = 0;
};class Assistant : public Student, public Teacher
{
public:Assistant(){cout << "Assistant构造" << endl;}int _task = 0;
};
int main()
{Assistant a;//a._name;//二义性:访问Student的_name还是Teacher的_name呢?//需要指定类域访问a.Student::_name = 1;a.Student::_age = 2;a._stuid = 3;a.Teacher::_name = 4;a.Teacher::_age = 5;a._jobid = 6;a._task = 7;return 0;
}

从a的内存布局可以看到,a中有两份_name和_age,它们是从Student和Teacher类继承下来的。二义性的问题可以通过指定类域访问解决,但数据冗余的问题是无法规避的,必须引入新的技术——虚继承 

二.虚继承的用法

只需在继承那个祖先类时加上关键字virtual即可

class Person
{public:Person(){cout << "Person构造" << endl;}
public:int _name = 0;int _age = 0;
};class Student :  virtual public Person
{public:Student(){cout << "Student构造" << endl;}int _stuid = 0;
};class Teacher :  virtual public Person
{
public:Teacher(){cout << "Teacher构造" << endl;}int _jobid = 0;
};class Assistant : public Student, public Teacher
{
public:Assistant(){cout << "Assistant构造" << endl;}int _task = 0;
};
int main()
{Assistant a;a.Student::_name = 1;a.Student::_age = 2;a._stuid = 3;a.Teacher::_name = 4;a.Teacher::_age = 5;a._jobid = 6;a._task = 7;return 0;
}

虚继承前:

虚继承后: 

可以看到,Person构造函数只调用了一次。

再来看看虚继承后a的内存分布:

 虚继承后,重复的那部分成员被单独拎了出来,只有一份,此时就不存在二义性的问题了。a.Student::_name;a.Student::_name;a._name访问的是同一份数据。同时也解决了数据冗余的问题。 

三.虚继承的原理

Student和Teacher中多出的这两个东西是什么呢?这似乎是一个地址,那我们在内存中看一看(注意是小端存储,低字节存低位数据,高字节存高位数据,故地址应该为007e9b4c和007e9b54)

注意这是16进制,故第一个数 是20,第二个数是12。

在看看上面的内存分布,会发现:006ff8d0这个地址加上20,006ff8d8加上12,刚好是006ff8e4,也就是重复的Person那部分变量的起始地址。

Assistant对象中将Person放到的了对象组成的最下面,这个Person同时属于Student和Teacher,给Student和Teacher都添加一个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存了偏移量,通过偏移量可以找到下面的Person。事实上,虚基表中存放了两个数据,第二个数是偏移量,第一个数与多态中的虚表有关,这里不作展开,后面的多态会讲到。

四.例题

1.多继承中指针偏移问题?下面说法正确的是( )

class Base1 {  public:  int _b1; };
class Base2 {  public:  int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main(){Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}

 A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

d的内存分布如图,p1和p3相等,不同之处在于p3解引用能访问整个对象,p1解引用只能访问Base1哪部分 

        

 2.下面程序输出结果是什么? ()

using namespace std;
class A{
public:A(const char *s) { cout<<s<<endl; }~A(){}
};
class B:virtual public A
{
public:B(const char *s1,const char*s2):A(s1) { cout<<s2<<endl; }
};
class C:virtual public A
{
public:C(const char *s1,const char*s2):A(s1) { cout<<s2<<endl; }
};
class D:public B,public C
{
public:D(const char *s1,char *s2,const char *s3,const char *s4):B(s1,s2),C(s1,s3),A(s1){ cout<<s4<<endl;}
};
int main() {D *p=new D("class A","class B","class C","class D");delete p;return 0;
}

A:class A class B class C class D

B:class D class B class C class A

C:class D class C class B class A

D:class A class C class B class D

首先明确一点,A的构造函数会调一次还是三次?因为虚继承解决数据冗余的问题,A的成员在D中只有一份,故只会调用一次构造函数。那么调用时机在哪呢?应该是在调用B,C的构造函数之前,A构造完后,构造B,C时就不会再去调用A的构造函数了。而B,C的构造函数谁先调用?答案是按继承顺序来,B先继承,故B先调用。故答案为A 

假如把两个virtual去掉,A的构造函数会调用几次?答案是编译错误,去掉virtual后这就是一个普通的多继承,B,C中都有A,D不能单独去初始化A。去掉初始化列表中的A(s1)就正确了,A会被构造两次。

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

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

相关文章

Unity的live2dgalgame多语言可配置剧情框架

这段代码用于读取表格 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using OfficeOpenXml; using System.IO; using UnityEngine.Networking; using UnityEngine.UI; using Random UnityEngine.Random;public class Plots…

使用AOP切面实现日志记录功能

系列文章 1.SpringBoot整合RabbitMQ并实现消息发送与接收 2. 解析JSON格式参数 & 修改对象的key 3. VUE整合Echarts实现简单的数据可视化 4. Java中运用BigDecimal对字符串的数值进行加减乘除等操作 5. List&#xff1c;HashMap&#xff1c;String,String&#xff1e;&…

将CSDN或Confluence文章转为微信公众号格式

最近在更公众号文章&#xff0c;苦于排版和格式&#xff0c;就找了一个比较方便的方法&#xff0c;简单易用&#xff0c;排版也不错。 文章提取 有的文章是已经发布在其它平台了&#xff0c;比如CSDN或Confluence&#xff0c;可以使用飞书剪存方便的将文章提取出来&#xff0…

垃圾回收系统小程序

在当今社会&#xff0c;废品回收不仅有利于环境保护&#xff0c;也有利于资源的再利用。随着互联网技术的发展&#xff0c;个人废品回收也可以通过小程序来实现。本文将介绍如何使用乔拓云网制作个人废品回收小程序。 1. 找一个合适的第三方制作平台/工具&#xff0c;比如乔拓云…

人工智能基础_机器学习001_线性回归_多元线性回归_最优解_基本概念_有监督机器学习_jupyter notebook---人工智能工作笔记0040

线性和回归,就是自然规律,比如人类是身高趋于某个值的概率最大,回归就是通过数学方法找到事物的规律. 机器学习作用: 该专业实际应用于机器视觉、指纹识别、人脸识别、视网膜识别、虹膜识别、掌纹识别、专家系统、自动规划、智能搜索、定理证明、博弈、自动程序设计、智能控制…

公众号迁移如何线上办理公证?

公众号账号迁移的作用是什么&#xff1f;只能变更主体吗&#xff1f;1.可合并多个公众号的粉丝、文章&#xff0c;打造超级大V2.可变更公众号主体&#xff0c;更改公众号名称&#xff0c;变更公众号类型——订阅号、服务号随意切换3.可以增加留言功能4.个人订阅号可迁移到企业名…

Go学习第十一章——协程goroutine与管道channel

Go协程goroutine与管道channel 1 协程goroutine1.1 基本介绍1.2 快速入门1.3 调度模型&#xff1a;MPG模式介绍1.4 设置cpu数1.5 协程资源竞争问题1.6 解决协程并发方案 2 管道channel2.1 基本介绍2.2 快速入门2.3 管道的关闭和遍历2.4 管道和协程的结合2.5 声明 只读/只写 的管…

最新SQL注入漏洞修复建议

点击星标&#xff0c;即时接收最新推文 本文选自《web安全攻防渗透测试实战指南&#xff08;第2版&#xff09;》 点击图片五折购书 SQL注入漏洞修复建议 常用的SQL注入漏洞的修复方法有两种。 1&#xff0e;过滤危险字符 多数CMS都采用过滤危险字符的方式&#xff0c;例如&…

小程序配置请求代理

在app.json中添加“proxy”字段配置代理 "proxy": {"/api": {"target": "http://192.168.110.249:8221/",//你要请求的目标地址"changeOrigin": true,"pathRewrite": {"^/api": ""//重定向}}…

【GIT】:一文快速了解什么是GIT

【GIT】&#xff1a;一文快速了解什么是GIT 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; 关于版本控制 什么是“版本控制”&#xff1f;我为什么要关心它呢&#xff1f; 版本控制是一种记录一个或若干文件内容变化&#xff0c;以便将来…

C++设计模式_14_Facade门面模式

本篇介绍的Facade门面模式属于“接口隔离”模式的一种&#xff0c;以下进行详细介绍。 文章目录 1. “接口隔离”模式1. 1 典型模式 2. 系统间耦合的复杂度3. 动机(Motivation)4. 模式定义5. Facade门面模式的代码实现6. 结构7. 要点总结8. 其他参考 1. “接口隔离”模式 在组…

如何为你的地图数据设置地图样式?

地图样式设置是GIS系统中非常重要的功能模块&#xff0c;水经微图Web版本最近对符号样式功能模块进行了升级。 你可以通过以下网址直接打开访问&#xff1a; https://map.wemapgis.com 现在我们为大家分享一下水经微图Web版中&#xff0c;如何为你标注的地图数据设置地图样式…

【C++进阶】pair容器

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

code too large

描述&#xff1a;比较尴尬&#xff0c;一个方法的代码接近10000行了&#xff0c;部署服务器的时候提示(java :code[255,21] too large),提示代码过长&#xff0c;无法运行。 查看了一下百度&#xff1a;解决的思路 JVM规范&#xff1a;「类或接口可以声明的字段数量限制在 655…

数据结构与算法基础(青岛大学-王卓)(9)

终于迎来了最后一部分(排序)了&#xff0c;整个王卓老师的数据结构就算是一刷完成了&#xff0c;但是也才是数据结构的开始而已&#xff0c;以后继续与诸位共勉 &#x1f603; (PS.记得继续守护家人们的健康当然还有你自己的)。用三根美味的烤香肠开始吧。。。 文章目录 排序基…

maya2023安装

1、解压压缩包&#xff0c;点击setup安装&#xff0c;除修改安装路径外&#xff0c;其他都是都是下一步&#xff0c;安装后最好重启系统 破解步骤 关闭杀毒&#xff0c;防止误删1.安装Autodesk软件&#xff0c;但是不要启动&#xff0c;安装完成后重启电脑 2.安装破解文件夹里…

Python轮廓追踪【OpenCV形态学操作】

文章目录 概要代码运行结果 概要 一些理论知识 OpenCV形态学操作理论1 OpenCV形态学操作理论2 OpenCV轮廓操作|轮廓类似详解 代码 代码如下&#xff0c;可以直接运行 import cv2 as cv# 定义结构元素 kernel cv.getStructuringElement(cv.MORPH_RECT, (3, 3)) # print kern…

星途星纪元 ES,用艺术思维表达工程技术

10月8日&#xff0c;星途星纪元ES携手世界级成都爱乐首席乐团、旅德青年钢琴家王超&#xff0c;在成都打造了一场“万物星声”超舒适音乐会视听盛宴。这是星途星纪元首次跨界音乐圈、牵手音乐挚友&#xff0c;共同演绎音乐和汽车的美学协奏曲&#xff0c;开启高端超舒适美学新纪…

汉威科技光纤预警系统,守护油气长输管道“大动脉”

石油、天然气早已成为城市生活中不可或缺的能源。广大车主能快速地加上汽油&#xff0c;千家万户能方便地用上天然气&#xff0c;得益于我国庞大的石油、天然气输送基础设施网络。 我国油气分布西多东少、北多南少&#xff0c;要想把千里、乃至万里之外的石油、天然气输送到中部…

竞赛 深度学习人脸表情识别算法 - opencv python 机器视觉

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人脸表情识别系…