更多课程 选择中心

C/C++培训
达内IT学院

400-111-8989

反汇编探索C++虚函数实现多态的机制

  • 发布:C++培训
  • 来源:学习笔记
  • 时间:2017-10-25 15:51

虚函数是C++实现多态的机制,那么它是如何做到的呢?

以下通过反汇编探索虚函数内存模型,查看虚函数实现多态的过程。

工具

Visual studio 2017:以下程序仅做VC++编译器下的32位程序探讨,其他编译器与64位程序所产生的差异不作讨论。

反汇编过程

首先声明一个不包含虚函数的简单C++类,如下:

在构造函数中加入断点,使得反汇编构造函数代码,如图:

当运行到断点时,在Visual Studio中使用快捷键Ctrl+F11打开反汇编,得出如下指令:

黄色框中的指令可以看出,构造函数中,将4这个值放到了this指针所指向区域的前4个字节,而4这个值刚好就是变量a的值,也就是说,在不包含虚函数时,4个字节长度的int型变量a就存放在对象数据空间的起始4个字节。

随后再创建包含虚函数的类c2,如图:

用相同方法让程序暂停在c2类的构造函数的断点,再反汇编,同时对比c1反汇编指令:

左边为包含虚函数的类c2,右边为不包含虚函数的类c1

可以看出,包含虚函数的类c2的构造函数,相比不包含虚函数的类c1,仅仅多出了两条指令:

这两条指令表示,this指针的前4个字节,不再是存放变量a,而是一个vftable的地址(即虚函数表),而变量数据则是通过往后偏移4个字节存放,如对比图左图所示,所有地址都以加4来偏移:

由此可以总结出包含虚函数的类对象在内存中的模型:

如图,包含虚函数的类对象,前4个字节存放的是虚函数表,后面才开始存放变量。

那么虚函数表中存放的是什么内容?

从上面图片中显示,虚函数表指向的内存地址为01 3B 8B 34h,通过Visual Studio的调试-窗口-内存,打开内存查看工具,定位到这个地址,可以看到里面的内容:

很明显可以看出,选中的8个字节分别代表两段内存地址01 3b 10 0a和01 3b 13 ac,而不是程序指令集,因为程序数据所在内存地址都在01 3b ** **内,可以看到所有反汇编指令前的内存地址皆是如此。

为了弄明白这两个内存地址的内容,在反汇编窗口输入内存地址查看01 3b 10 0a:

可以看到,这个地址指向第一个虚函数c2::test(),再看第二个地址01 3b 13 ac:

可以看到,第二个地址指向第二个虚函数c2::test2()。

这说明,虚函数表实际上是一个存放虚函数指针的数组。

那么多态是怎么实现的呢?

再创建一个继承c2的子类cc2,同样反汇编查看构造函数:

可以看出,cc2构造函数中赋予的虚函数表地址与基类地址不同,并且所指向的虚函数也是cc2自己的虚函数,在cc2的构造函数中,先调用了基类c2的构造函数,如红色框所示,在基类c2的构造函数中,会将基类的虚函数表放在类对象前4个字节,随后从c2构造函数中出来,运行到黄色框部分,子类会再次将子类自己的虚函数表放在类对象前4个字节,从而将基类的虚函数表覆盖,实现多态。

总结

不包含虚函数的类,是没有虚函数表的,变量等数据是从类对象数据块的第一个字节开始存放。

包含虚函数的类,会多出一个虚函数表,而类对象数据块的前4个字节存放的是虚函数表地址,从第5个字节开始存放变量等数据。

虚函数表实质是一个函数指针的数组,存放着本类的各个虚函数的指针。

由于基类的构造函数先于子类执行,这会导致子类的虚函数表巧妙地覆盖掉基类的虚函数表,从而实现多态。

本文内容转载自网络,本着分享与传播的原则,版权归原作者所有,如有侵权请联系我们进行删除!

预约申请免费试听课

填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!

上一篇:C++11中的内存模型详解
下一篇:C++中static用法介绍

C语言创建windows窗口实例

C++回调函数是什么?

C++ shared_ptr和动态数组

C语言有哪些关键词,C语言44个关键词大全

  • 扫码领取资料

    回复关键字:视频资料

    免费领取 达内课程视频学习资料

  • 搜索抖音号

    搜索抖音号:1821685962

    免费领取达内课程视频学习资料

Copyright © 2021 Tedu.cn All Rights Reserved 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有

选择城市和中心
黑龙江省

吉林省

河北省

湖南省

贵州省

云南省

广西省

海南省