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

这篇具有很好参考价值的文章主要介绍了【C++】 为什么多继承子类重写的父类的虚函数地址不同?『 多态调用汇编剖析』。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

👀樊梓慕:个人主页

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

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


前言

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

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

你有没有发现:子类重写的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.剖析

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

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

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

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

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

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

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

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

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

接下来我们转到反汇编:

2.1Base1类型的p1指针调用func1 

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

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

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

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

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

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

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

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

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

2.2Base2类型的p2指针调用func1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

好,成功调用到func1函数。


3.总结

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

为什么呢?

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

😈剖析底层,修炼内功😈


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

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

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

🌟~ 点赞收藏+关注 ~🌟

=========================================================================
 文章来源地址https://www.toymoban.com/news/detail-825588.html

到了这里,关于【C++】 为什么多继承子类重写的父类的虚函数地址不同?『 多态调用汇编剖析』的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • java子类继承父类方法、或者接口中方法的javadoc注释

    java子类继承父类方法、或者接口中方法的javadoc注释

    详情可以阅读: https://docs.oracle.com/en/java/javase/19/docs/specs/javadoc/doc-comment-spec.html#method-comment-inheritance 子类继承父类、或者子类实现接口,在子类中为了避免重复写注释,可以在子类方法注释的主要描述部分、或者@return、@param、@throws标记后面的文本参数部分插入{@inheritDoc}标记

    2024年02月11日
    浏览(18)
  • Java中为什么重写equals()也需要重写hashCode()?

    所有类默认继承Object类 先看一下Object源码 结论 equals()返回结果为true hashCode()一定相同 \\\'==\\\'结果为true hashCode()一定相同 \\\'==\\\'结果为false hashCode()有可能相同(哈希冲突) hashCode()重写规范就是保证equals()相同的两个对象拥有相同的哈希值 回到刚刚哪个问题 为什么重写equals还要重写

    2023年04月25日
    浏览(4)
  • 为什么Java不支持多继承

    ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:每天一个知识点 ✨特色专栏: MySQL学习 🥭本文内容:为什么Java不支持多继承 📚个人知识库: Leo知识库,欢迎大家访问 Java不支持多继承的主要

    2024年04月13日
    浏览(6)
  • 为什么 Java 中不允许多继承?

    为什么 Java 中不允许多继承?

    思考现在既定事实背后的原因 多继承会导致“钻石问题” 类 B、C 继承父类 A,类 D 同时继承了 B 和 C(假设存续多继承) 下述图表的形状类似于钻石(或者菱形),因此这个问题被形象地称为钻石问题(菱形继承问题) 以上述例子说明: 类 D 初始化时,会执行两次类 A 的初

    2023年04月14日
    浏览(9)
  • 为什么Java要求多用组合,少用继承?

    为什么Java要求多用组合,少用继承?

    最近有一个要好的朋友去阿里大厂面试Java高级工程师,在第二轮面试的过程中,面试官提了一个问题:“解释下Java项目中为什么要求多用组合,少用继承?”。朋友觉得这个题目既熟悉,又陌生,毕竟继承与组合在一开始学习Java时就要求掌握的,但是教学过程中只是说了它

    2024年02月05日
    浏览(7)
  • c++和QT子类调用父类方法

    c++调用方式 : 父类名::方法名 QT调用方式 : __super::方法

    2024年02月13日
    浏览(5)
  • 我在VScode学Java继承(Java继承的特点、super关键字、super和this对比、方法重写、子类构造器)贰

    我在VScode学Java继承(Java继承的特点、super关键字、super和this对比、方法重写、子类构造器)贰

    类的成员包括:成员变量;成员方法;构造方法 构造方法:不管什么修饰符都不可继承 成员变量:都可以继承,但注意一点(继承!=调用,私有的就不可调用) 成员方法:非私有可以。私有不可。 我的个人博客主页:如果’\\\'真能转义1️⃣说1️⃣的博客主页 关于Java基本语

    2024年02月14日
    浏览(36)
  • 【从JVM看Java,三问继承和多态,是什么?为什么?怎么做?深度剖析JVM的工作原理】

    《计算机底层原理专栏》:欢迎大家订阅学习,能够帮助到各位就是对我最大的鼓励! 文章目录 系列文章目录 前言 一、JVM是什么 二、 什么是继承 三、 什么是多态 总结         这篇文章聚焦JVM的实现原理,我更专注于从一个语言的底层原理,去剖析他的语法所实现的意义

    2024年02月05日
    浏览(8)
  • 为什么要学习C++

    为什么要学习C++

    UINX操作系统诞生之初是用汇编语言编写的。 随着UNIX的发展,汇编语言的开发效率成为一个瓶颈。 寻找新的高效开发语言成为UNIX开发者需要解决的问题。 当时BCPL语言成为了当时的选择之一。 Ken Thomposn对BCPL进行简化得到了B语言。 但是B语言不是直接生成机器码,而是生成中

    2024年02月10日
    浏览(6)
  • 为什么 C 语言没有被 C++ 取代?

    为什么 C 语言没有被 C++ 取代?

    今日话题,为什么 C 语言没有被 C++ 取代?C语言之所以没有被C++完全取代,有几个主要原因。首先,C++的编译器实现相对复杂,这对于一些嵌入式平台来说是一个问题。许多嵌入式系统只支持C语言,因此C++在这些平台上无法使用。即使在支持C++的嵌入式平台上,也常常存在不

    2024年01月19日
    浏览(13)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包