【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的操作。

😈剖析底层,修炼内功😈


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

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

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

🌟~ 点赞收藏+关注 ~🌟

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

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

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

相关文章

微信小程序框架阐述

目录 一、框架 响应的数据绑定 页面管理 基础组件 丰富的 API 二、逻辑层 App Service 小程序的生命周期 注册页面 使用 Page 构造器注册页面 在页面中使用 behaviors 使用 Component 构造器构造页面 页面的生命周期 页面路由 页面栈 路由方式 注意事项 模块化…

鸿蒙开发系列教程(二十二)--List 列表操作(1)

列表是容器&#xff0c;当列表项达到一定数量&#xff0c;内容超过屏幕大小时&#xff0c;可以自动提供滚动功能。 用于呈现同类数据类型或数据类型集&#xff0c;例如图片和文本 List、ListItemGroup、ListItem关系 列表方向 1、概念 列表的主轴方向是指子组件列的排列方…

【汇总】解决IndexedDB报Failed to execute ‘transaction‘ on ‘IDBDatabase‘

问题发现 再学习HTML5中&#xff0c;有介绍到 Web 存储&#xff0c;当代码编写完成后&#xff0c;运行报错 Failed to execute ‘transaction’ on ‘IDBDatabase’: One of the specified object stores was not found. 示例代码如下&#xff1a; <!DOCTYPE html> <…

1Coze平台介绍

2023年随着OpenAI推出GPT 3.5&#xff0c;AI赛道变得更加火热。GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一种自然语言处理&#xff08;NLP&#xff09;模型&#xff0c;用于生成文本、理解语言和进行各种语言任务。GPT是由OpenAI开发的&#xff0c;它…

Tuxera NTFS2024版本的文件操作功能有哪些特点?

Tuxera NTFS通过集成先进的文件系统驱动程序和算法&#xff0c;实现了对多种文件系统的全面支持。具体来说&#xff0c;它具备以下功能和特点&#xff0c;使其能够支持多种文件系统&#xff1a; Tuxera NTFS2024下载如下: https://wm.makeding.com/iclk/?zoneid58824 先进的…

shumei 滑块 qd参数仿写记录

在对qd参数进行仿写的过程中&#xff0c;由于缺失很多js的基础知识&#xff0c;导致进展一度非常的缓慢&#xff0c;并且不知道自己的方向是不是正确的方向。在不知道自己的方向是否正确的时候&#xff0c;这个时候自己的投入的努力都是畏首畏尾。大概是一种&#xff0c;不知道…

基于剪贴板的文件传输方案

文章目录 背景原理步骤获取待上传文件的十六进制数据格式转换输出 背景 某次误删了环境上的C库之后想到的 什么都不可用了&#xff0c;但当前的ssh连接还在&#xff0c;echo命令和重定向符还可以使用 这就催生了我的想法&#xff1a;直接用echo -en “\xab\xcd” > file这样…

【c++】析构函数

1.特征 析构函数是特殊的成员函数&#xff0c;其特征如下&#xff1a; 1.析构函数名是在类名前加上字符~。 2.无参数无返回值类型。 3.一个类只能有一个析构函数。若未显式定义&#xff0c;系统会自动生成默认的析构函数。注意&#xff1a;析构函数不能重载。 4.对象生命周…

H12-821_48

48.下面是台路由器输出的BGP信息,关于这段信息描述措误的是 A.路由器的Router ID是1.1.1.9 B.display bgp network命令来显示BGP通过network ( BGP)的通告的路由信息 C.该路由器所在AS号是10 D.该路由器通过import-route命今引入了4.4.4.0/24的网段 答案&#xff1a;D 注释&am…

假期作业 10

1.整理磁盘操作的完整流程&#xff0c;如何接入虚拟机&#xff0c;是否成功识别&#xff0c;对磁盘分区工具的使用&#xff0c;格式化&#xff0c;挂载以及取消挂载 U盘接入虚拟机 在虚拟机--->可移动设备--->找到U盘---->连接 检测U盘是否被虚拟机识别 ls /dev/s…

Diffusion Transformer U-Net for MedicalImage Segmentation

用于医学图像分割的扩散变压器U-Net 摘要&#xff1a; 扩散模型在各种发电任务中显示出其强大的功能。在将扩散模型应用于医学图像分割时&#xff0c;存在一些需要克服的障碍:扩散过程调节所需的语义特征与噪声嵌入没有很好地对齐;这些扩散模型中使用的U-Net骨干网对上下文信…

Codeforces Round 926 (Div. 2)(A~C)

A. Sasha and the Beautiful Array 分析&#xff1a;说实话&#xff0c;打比赛的时候看到这题没多想&#xff0c;过了一下样例发现将数组排序一下就行&#xff0c;交了就过了。刚刚写题解反应过来&#xff0c;a2-a1a3-a2.....an-a(n-1) an - a1&#xff0c;所以最后结果只取决…

python工具方法 45 基于ffmpeg以面向对象多线程的方式实现实时推流

1、视频推流 参考基于ffmpeg模拟监控摄像头输出rtsp视频流并opencv播放 实现视频流的推流。 其基本操作就是,安装视频流推流服务器,ffmpeg,准备好要推流的视频。 命令如下所示:ffmpeg -re -stream_loop -1 -i 风景视频素材分享.flv -c copy -f rtsp rtsp://127.0.0.1:554/…

怎么搭建自己的网站?

怎么搭建自己的网站 一.领取一个免费域名和SSL证书&#xff0c;和CDN 特点&#xff1a;支持Cloudflare CDN Cloudflare是全球知名的CDN提供商&#xff0c;如果你不想暴露你的源站&#xff0c;又想使用我们的二级域名&#xff0c;不需要前往Cloudflare添加域名&#xff0c;修…

【机器学习笔记】 6 机器学习库Scikit-learn

Scikit-learn概述 Scikit-learn是基于NumPy、 SciPy和 Matplotlib的开源Python机器学习包,它封装了一系列数据预处理、机器学习算法、模型选择等工具,是数据分析师首选的机器学习工具包。 自2007年发布以来&#xff0c;scikit-learn已经成为Python重要的机器学习库了&#xff…

家里台式机需要把一个网站发布到外网可用,怎么搞?--DDNS配置(动态域名解析配置)

前言 Dynamic DNS是一个DNS服务。当您的设备IP地址被互联网服务提供商动态变更时,它提供选项来自动变更一个或多个DNS记录的IP地址。 此服务在技术术语上也被称作DDNS或是Dyn DNS 如果您没有一个静态IP,那么每次您重新连接到互联网是IP都会改变。为了避免每次IP变化时手动更…

java数据结构与算法刷题-----LeetCode18. 四数之和

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 解题思路 此题为三数之和的衍生题&#xff0c;代码完全一样&#xff0c;只…

一、Java安装JDK8

Java安装JDK8 一、Centos7.91.命令行安装2.安装包安装2.1 下载2.2 安装2.3 配置java环境 二、Windows1.下载2.安装3.配置环境变量 一、Centos7.9 1.命令行安装 yum install java-1.8.0-openjdk.x86_642.安装包安装 2.1 下载 下载地址&#xff1a;https://www.oracle.com/cn…

基于T1w/T2w 比值揭示髓磷脂相关变化

前言&#xff1a; 最近在阅读文献的时候发现2篇文章&#xff0c;是采用T1w/T2w 比值表征髓磷脂&#xff0c;有点感兴趣&#xff0c;因此尝试了一下文献所提出的方法。&#xff08;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9247578/ https://www.ncbi.nlm.nih.gov/pmc/ar…

鸿蒙开发系列教程(二十四)--List 列表操作(3)

列表编辑 1、新增列表项 定义列表项数据结构和初始化列表数据&#xff0c;构建列表整体布局和列表项。 提供新增列表项入口&#xff0c;即给新增按钮添加点击事件。 响应用户确定新增事件&#xff0c;更新列表数据。 2、删除列表项 列表的删除功能一般进入编辑模式后才可…