c++——多态(补充)

优先查看:c++——多态_Hiland.的博客-CSDN博客


目录

菱形虚拟继承子类的重写问题

菱形虚拟继承中的偏移量补充

逆向思维——汇编查看多态中被重写的虚函数


菱形虚拟继承子类的重写问题

继承环节时,菱形虚拟继承解决了菱形继承的数据冗余和二义性问题。菱形虚拟继承在多态中使用会出现一些问题,有如下代码:

class A
{
public:virtual void func() {}int _a;
};
//继承A
class B : virtual public A
{
public:virtual void func() {}virtual void func1() {}int _b;
};
//继承A
class C : virtual public A
{
public:virtual void func() {}int _c;
};
//继承B和继承C
//当B和C都采用虚继承继承A,并且重写了A中的func虚函数,D继承了B和C,D必须重写A中的func虚函数
//否则D无法知道将B的虚函数写入虚表中还是将C的虚函数写入虚表中
class D : public B,  public C
{
public://必须重写func//virtual void func() {}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;
}

运行如上代码,编译器报错,报错如下:

当B和C都采用虚继承继承A,并且重写了A中的func虚函数,D继承了B和C,D必须重写A中的func虚函数,否则D无法知道将B的虚函数写入虚表中还是将C的虚函数写入虚表中,因此D必须重写func函数。

菱形虚拟继承中的偏移量补充

(1条消息) c++——继承_Hiland.的博客-CSDN博客

将上述代码修改后运行并查看内存窗口里的偏移量时,之前发现偏移量上方还有一个00000000的数据,在多态时使用到了该数据。如下图:

此时由于B类增加了一个虚函数,所以B中有一个对应的虚表,根据内存窗口查看得知第二个就是之前在继承阶段每个类距离a的偏移量,查询b中的偏移量地址,发现如下图:

本该在继承阶段的那个00000000 变成了fcffffff,这个其实是该类到虚表的偏移量,所以该值是在多态中使用到的。

逆向思维——汇编查看多态中被重写的虚函数

当我们在vs下针对多继承问题想查看子类后面的虚函数时会使用以下代码:

class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Derive d;VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);//Base1类PrintVTable(vTableb1);VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));//Base2类PrintVTable(vTableb2);return 0;
}

我们已经查看了派生类的虚函数也进了虚表中,但是在vs的监视窗口中我们发现了基类和派生类的func1函数地址不同,在debug下查看监视窗口,出现如下图的情况:

同样是在d的派生类对象中,Base1部分的func1函数和Base2部分的func1函数地址不同,Base1部分func2函数和Base2部分的func2函数不同,在这里以func1函数为例探索,先在main函数中打印d对象中的func1函数地址看看:

对比打印虚表中的Base1部分的func1函数,虚表中的Base2部分的func1函数,以及直接打印d对象中func1函数,发现Base1和Base2中的函数地址不一样且和d对象中的func1函数地址都不一样

为了查看究竟是发生了什么,我们在vs下进行反汇编,通过汇编过程查看Base1和Base2中分别在调用func1的过程

通过反汇编查看的Base1中的func1函数的调用:

 

通过反汇编查看的Base2中的func1函数的调用:

通过以上的Base1和Base2两个类分别调用func1,能够得到以下的结论:

这里运用的就是逆向思维,通过汇编代码来查看一些编译器中出现的无法理解的现象。

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

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

相关文章

SQL性能规范

一、随聊 记录一下吧&#xff0c;2023年7月13日00:11:11&#xff0c;现在的状态真的很&#xff0c;忙&#xff0c;干不完的活&#xff0c;希望巨大的压力&#xff0c;能够让自己快速成长&#xff0c;回想我这一路&#xff0c;21年大专毕业&#xff0c;用一年时间熟悉软件&…

python中应用requests库模拟postman请求携带token,使用get和post方法请求头携带token

背景&#xff1a; 实际开发中&#xff0c;Python程序中需要调用后台接口&#xff0c;充当前端&#xff0c;后端规定请求头需要携带token 封装的get和post类: class RequestMethodCarryJson:"""定义请求类型以json方式传递参数"""def __init__…

nodejs 下载地址 阿里云开源镜像站

nodejs 下载地址 阿里云开源镜像站 https://mirrors.aliyun.com/nodejs-release/ 我们下期见&#xff0c;拜拜&#xff01;

【LSTM】理解LSTM

原文&#xff1a;https://colah.github.io/posts/2015-08-Understanding-LSTMs/ 递归神经网路 Humans don;t start their thinking from scratch every second. 人类都不是每一秒都从零开始思考。 from scratch 从零开始 当你读到这篇文章时&#xff0c;你理解每个单词时&a…

全志F1C200S嵌入式驱动开发(sd卡驱动)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 说是sd卡,其实是micro sd卡,或者称之为tf卡更合适。一般的soc都支持从tf卡启动,所以用tf卡来学习soc、驱动和linux,对新人来说是比较合适的。前面我们已经用sd卡构建了一个类似…

【3-D深度学习:肺肿瘤分割】创建和训练 V-Net 神经网络,并从 3D 医学图像中对肺肿瘤进行语义分割研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Spring方式发送邮箱

1.导入依赖 <!--邮件发送依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency> 2.导入工具类 package com.example.demo.demo;import org.springfram…

基于单片机智能饮水机加热系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前水温&#xff0c;定时提醒&#xff0c;水量变化DS18B20检测当前水体温度&#xff1b;水位传感器检测当前水位&#xff1b;继电器驱动加热片进行水温加热&#xff1b;定时提醒喝水&#xff0c;蜂鸣器报警&#x…

plt.text()函数解析

plt.text(x, y, s, fontsize, verticalalignment,horizontalalignment,rotation , *kwargs) 参数&#xff1a; x,y:表示坐标值上的值s:表示说明文字fontsize:表示字体大小verticalalignment&#xff1a;垂直对齐方式 &#xff0c;参数&#xff1a;[ ‘center’ | ‘top’ | ‘…

vue-cesium的基本使用【一】

最近的项目中用到了cesium,也了解了一点关于cesium的知识&#xff0c;打点、 标绘、等等基础的功能点&#xff0c;但是在开发过程中使用原生的cesium编写对于初学者还是有点难度&#xff0c;为此&#xff0c;找到关于对cesium进行二次封装的开源项目vue-cesium,本次文章主要记录…

git 工具使用--分支管理

git 工具使用–分支管理 文章目录 git 工具使用--分支管理理解分支创建分支切换分支合并分支删除分支合并冲突分支管理策略分支策略bug分支删除临时分支总结 理解分支 分支管理是Git的杀手级功能之一。分支&#xff1a;就是科幻中的平行宇宙&#xff0c;当你正在电脑面前学习C…

基于Cyclone V SoC利用HLS实现卷积手写体数字识别设计

基于Cyclone V SoC利用HLS实现卷积手写体数字识别设计 本文是基于英特尔 Cyclone V SoC 开发板&#xff0c;利用 HLS 技术实现三层卷积两层池化两层全连接推理运算的手写体数字识别设计 硬件环境&#xff1a; Cyclone V SoC开发板 SD卡 电脑 软件环境&#xff1a; Windows 11 Q…