【C++】 为什么多继承子类重写的父类的虚函数地址不同?『 多态调用汇编剖析』

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


前言

本篇文章主要是为了解答有关多态的那篇文章那块的一个奇怪现象,大家还记得这张图片么?

你有没有发现:子类重写的func1函数地址竟然是不同的?

按常理讲:我们知道函数地址存储的是函数的指令的位置,这里『 应该是相同』的,才能保证对象在调用时都调用『 子类重写后的』func1方法 ,否则就失去了重写的意义了。

所以这里一定存在某些底层设计,那接下来就让我们转到『反汇编 』,来查看以下vs在这里是如何设计的吧。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟

=========================================================================


1.构建模型

首先,为了方便研究,我们构建函数模型:

class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1=1;
};

class Base2 {
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2=2;
};

class Derive : public Base1, public Base2 {
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1=3;
};

int main()
{
	Derive d;

	// 多态调用
	Base1* p1 = &d;
	p1->func1();

	Base2* p2 = &d;
	p2->func1();

	return 0;
}

2.剖析

通过内存窗口我们得出这样的结构:

经过多态部分的学习,我们知道p1指针指向的对象内存中『 0x00819b94 』就是Base1的虚表指针,同样的p2指针指向的对象内存中『 0x00819ba8』就是Base2的虚表指针。

监视窗口也可以看出来这些:

注意:多态那篇文章我们已经提到过,vs的监视窗口这里有一个bug,就是没能显示出子类func3函数, 即子类d的虚函数表没有显示在监视窗口中,这里大家可以参考我多态部分的文章有详解,你也可以自己通过内存窗口验证。子类的虚函数表添加在继承的第一个父类的虚表后。

当然以上说的不是我们这篇文章的重点,只是一个回顾,更多详见『 樊梓慕』多态 - CSDN

我们主要研究func1函数的地址为什么是不同的? 

接下来我们转到反汇编:

2.1Base1类型的p1指针调用func1 

首先观察下p1调用func1的汇编代码,看看call到了哪里?

寄存器eax中存储的是『 0x00811230』,我们接着走:

很明显是一个jmp指令,再继续:

到这,我们就成功跳转到了子类重写的func1函数。

这也是正常情况下函数调用的过程。

那接下来我们来研究p2指针调用func1又是怎样的过程呢?

2.2Base2类型的p2指针调用func1

同样的eax中存储的地址是什么呢?继续往下走:

 同样是一个jmp指令,再继续:

这里jmp到了给ecx减8,然后再jmp,在ecx减8之前,我们先来看看ecx中存储的是什么:

注意:成员函数的调用中,寄存器ecx通常用来存储this指针

那减去8之后,很明显就变成了『 0x00fcfba0』,这个地址是什么呢?

其实就相当于子类Derive的this指针。

到这其实已经非常明显了,那我们继续看jmp到了哪里:

到这里,你有没有发现这步的jmp和Base1类型的p1指针调用func1的jmp已经完全一样了,继续:

好,成功调用到func1函数。


3.总结

总结一下,Base2类型的p2指针调用func1函数时多做了一些工作,多jmp了一步,jmp的这一步目的是为了调整this指针,让this指针指向子类的头部。

为什么呢?

  • 因为p1和p2都是父类指针指向子类对象,p1是因为巧合恰好与子类头部位置重合,所以this指针位置本就是正确的,不需要额外操作。
  • 而p2的this指针指向的位置是子类中自己的虚表位置,所以需要额外jmp一步,使p2指针指向的子类对象的this指针进行一定的偏移,让this指针到达正确的位置,才能完成调用func1的操作。

😈剖析底层,修炼内功😈


=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode Python -18.四数之和

目录 题目答案运行结果 题目 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#x…

OpenTitan- 开源安全芯片横空出世

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

前端网络安全笔记

本文主要涉及6个内容&#xff1a; HTTP与HTTPS同源策略&#xff08;Same-origin policy&#xff0c;简称 SOP&#xff09;/ 跨域资源共享&#xff08;Cross-Origin Resource Sharing&#xff0c;简称 CORS&#xff09;跨站脚本攻击&#xff08;Cross-Site Scripting&#xff0…

Java中的String类的常用方法(对于字符串的常用操作)

目录 一、获取指定索引的字符 二、 获取指定字符或者字符串的索引位置 三、判断字符串是否以指定内容开头或结尾 四、替换指定的字符或者是字符串 五、获取字符串的子串 六、将字符串转换为字符数组 七、比较字符串的内容是否相等 八、连接字符串 九、比较两个字符串的大…

JVM(5)面试篇

1 什么是JVM&#xff1f; 关联课程内容 基础篇-初识JVM基础篇-Java虚拟机的组成 回答路径 JVM的定义作用功能组成 1、定义&#xff1a; JVM 指的是Java虚拟机&#xff08; Java Virtual Machine &#xff09;。JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是…

ArduPilot开源飞控之硬件SBC分析

ArduPilot开源飞控之硬件SBC分析 1. 源由2. Companion Computer2.1 APSync【不推荐&#xff0c;无更新】2.2 DroneKit【不推荐&#xff0c;无更新/SDK】2.3 FlytOS【不推荐&#xff0c;闭源】2.4 Maverick【不推荐&#xff0c;闭源】2.5 ROS【专门讨论&#xff0c;开源/复杂】2…

C/C++重点解析——内存管理

1. C/C内存分布 我们先来看一段代码和其相关问题&#xff1a; int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd"…

HGAME2024 WEEK2 wp webmisc

web What the cow say? 进入容器有个输入框&#xff0c;尝试ssti、命令执行、代码执行等&#xff0c;最后发现可使用反引号执行命令&#xff1b; 输入 nl app.py 可查看源代码&#xff0c;有功能具体实现、过滤之类的&#xff1b; flag在 /flag_is_here home/flag_c0w54y 中…

【NLP】MHA、MQA、GQA机制的区别

Note LLama2的注意力机制使用了GQA。三种机制的图如下&#xff1a; MHA机制&#xff08;Multi-head Attention&#xff09; MHA&#xff08;Multi-head Attention&#xff09;是标准的多头注意力机制&#xff0c;包含h个Query、Key 和 Value 矩阵。所有注意力头的 Key 和 V…

AI中的直方图均衡

目标 在本教程中&#xff0c;您将学习&#xff1a; 什么是图像直方图以及它为什么有用使用 OpenCV 函数 cv&#xff1a;&#xff1a;equalizeHist 均衡图像的直方图 理论 什么是图像直方图&#xff1f; 它是图像强度分布的图形表示。它量化了所考虑的每个强度值的像素数。…

搜索专项---最短路模型

文章目录 迷宫问题武士风度的牛抓住那头牛 一、迷宫问题OJ链接 本题思路:只需要记录各个点是有哪个点走过来的&#xff0c;就能递推得出路径。记录前驱假设从 1,1 这个点向下走到了2, 1&#xff0c;则将2,1这个点的前驱记为1,1。这样&#xff0c;将整张地图 bfs 后&#xff0c…

[嵌入式系统-14]:常见实时嵌入式操作系统比较:RT-Thread、uC/OS-II和FreeRTOS、Linux

目录 一、实时嵌入式操作系统 1.1 概述 1.2 什么“实时” 1.3 什么是硬实时和软实时 1.4 什么是嵌入式 1.5 什么操作系统 二、常见重量级操作系统 三、常见轻量级嵌入式操作系统 3.1 概述 3.2 FreeRTOS 3.3 uC/OS-II 3.4 RT-Thread 3.5 RT-Thread、uC/OS-II、Free…

LGAMEFI基于BPL公链开发的第一生态:开启RWA游戏娱乐与DeFi融合的新纪元

在去中心化金融&#xff08;DeFi&#xff09;与游戏娱乐的结合趋势中&#xff0c;BPL公链上的LGAMEFI项目代表了前沿的技术革新和市场领导。这种将web2上成熟页游进行RWA链改&#xff0c;不仅仅是将游戏热门领域融合&#xff0c;更是在寻找一种全新的参与者经验&#xff0c;将玩…

Pod 和容器的设计模型

一、为什么需要 Pod&#xff1a; 1、容器的基本概念&#xff1a; 容器的本质实际上是一个进程&#xff0c;是一个视图被隔离&#xff0c;资源受限的进程。容器里面 PID1 的进程就是应用本身&#xff0c;这意味着管理虚拟机等于管理基础设施&#xff0c;但管理容器却等于直接管…

拿捏单链表

目录 引言 一&#xff1a;链表的定义 二&#xff1a;单链表的定义 三&#xff1a;单链表的增删查改 1.单链表增删查改及遍历的声明 注&#xff1a;在测试中创建指向头结点的指针plist 2.二级指针应用的说明 3.单链表的遍历 4.创建节点 5.单链表的插入 (1)头插 …

Linux操作系统——命名管道

我们前面说的管道都是只能具有血缘关系的进程进行进程间通信&#xff0c;如果我想让两个毫不相干的进程进行通信呢&#xff1f;那就需要来谈谈命名管道了。 命名管道 管道应用的一个限制就是只能在具有共同祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。如果我们想…

软考 系统分析师系列知识点之信息系统战略规划方法(11)

接前一篇文章&#xff1a;软考 系统分析师系列知识点之信息系统战略规划方法&#xff08;10&#xff09; 所属章节&#xff1a; 第7章. 企业信息化战略与实施 第4节. 信息系统战略规划方法 7.4.7 价值链分析法 价值链分析&#xff08;Value Chain Analysis&#xff0c;VCA&am…

【C++】---类和对象(中)默认成员函数 和 操作符重载

前言&#xff1a; 假如一个类中既没有成员变量也没有成员函数&#xff0c;那么这个类就是空类&#xff0c;空类并不是什么都没有&#xff0c;因为所有类都会生成如下6个默认成员函数&#xff1a; 一、构造函数 1、构造函数的定义及其特性 对于日期类对象&#xff0c;我们可…

pytest教程-10-allue2生成html报告

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest-html生成html报告的方法&#xff0c;本小节我们讲解一下使用allue2生成html报告。 自动化测试执行完成后我们需要展示给其他人看&#xff0c;这就要有自动化测试报告了。复杂的测试报告…

稳态准直太阳光模拟器

概述 稳态准直太阳光模拟器是一种用于模拟太阳光的设备。它能够产生高强度、高稳定性的太阳光&#xff0c;用于太阳能电池、太阳能热利用等领域的研究和实验。 稳态准直太阳光模拟器通常由以下几个部分组成&#xff1a; 光源&#xff1a;采用强度和颜色温度与太阳光接近的光源…