您现在的位置是:群英 > 开发技术 > 编程语言
C++中虚函数调用的实现代码是什么
Admin发表于 2022-08-22 17:58:37938 次浏览
这篇文章给大家介绍了“C++中虚函数调用的实现代码是什么”的相关知识,讲解详细,步骤过程清晰,对大家进一步学习和理解“C++中虚函数调用的实现代码是什么”有一定的帮助,希望大家阅读完这篇文章能有所收获。下面就请大家跟着小编的思路一起来学习一下吧。


虚函数(代码段地址)被存放在虚函数表中,调用虚函数的流程是这样子的:先获取虚函数表的首地址,然后根据目标虚函数在虚函数表的位置(offset偏移)取出虚函数表中的虚函数地址,最后去call这个虚函数(地址),就完成虚函数的调用。这个虚函数调用的流程在汇编代码中可以最直观的反映出来。

在排查软件异常或崩溃时,我们时常要借助汇编代码的上下文去辅助分析问题。读懂C++虚函数调用的汇编代码实现,对于搞懂汇编代码的上下文时很有好处的。今天我们就来看看虚函数调用的汇编代码实现。

比如如下的C++代码:

// 1、虚接口类中定义了纯虚接口
class IContactInterface
{
    //...
 
    virtual BOOL IsLoadFinish() == 0;  // 虚接口
 
    //...
}
 
 
// 2、子类中实现虚接口
class Contact : public IContactInterface
{
    //...
 
    BOOL IsLoadFinish(); 
 
    //...
}
 
 
// 3、new出子类的对象,存放到父类的指针变量中
IContactInterface* g_pContactPtr = new Contact;
 
 
// 4、获取子类对象的接口实现
IContactInterface* GetContactPtr()
{
    return g_pContactPtr;
} 
 
 
// 5、调用GetContactPtr获取子类的对象去调用虚函数IsLoadFinish
GetContactPtr()->IsLoadFinish();

上述C++代码片中,主要包含了以下几点:

1)定义了虚接口类IContactInterface,在类中有个IsLoadFinish虚函数;

2)定义了一个子类Contact,继承于IContactInterface接口类,并实现了虚函数IsLoadFinish;

3)new出一个Contact类的对象,赋值给父类IContactInterface的指针变量;

4)调用GetContactPtr接口获取IContactInterface指针变量,调用虚函数IsLoadFinish。

其中,GetContactPtr()->IsLoadFinish()这句虚函数调用的汇编代码如下所示:

.text:005103EB call ds:__imp__GetContactPtr
.text:005103F1 mov [ebp+var_2AF4], eax
.text:005103F7 mov ecx, [ebp+var_2AF4]
.text:005103FD mov edx, [ecx]
.text:005103FF mov ecx, [ebp+var_2AF4]
.text:00510405 mov eax, [edx+78h]
.text:00510408 call eax

现在我们就来详细解读一下这段实现虚函数调用的汇编代码。

1、汇编代码段1:

.text:005103EB call ds:__imp__GetContactPtr
.text:005103F1 mov [ebp+var_2AF4], eax
.text:005103F7 mov ecx, [ebp+var_2AF4]

调用GetContactPtr接口,返回IContactInterface*指针,其中存放的是子类Contact对象。GetContactPtr接口返回的IContactInterface*指针中的值,放到eax寄存器中。

在C++的汇编代码中,调用函数的返回值是放到eax寄存器中的。

所以eax中存放的是子类Contact对象的地址。然后将eax中存放的子类Contact对象的地址,放到函数栈内存[ebp+var_2AF4]中。然后又将子类Contact对象的地址放到ecx寄存器中。

2、汇编代码段2:

.text:005103FD mov edx, [ecx]

代码运行至此,此时ecx中存放的就是子类Contact对象的地址。该句汇编代码是以ecx中的值作为内存地址,取出该地址中的值放到edx寄存器中。

在C++中,C++类中如果有虚函数,则类中会掩藏一个虚函数指针成员变量,是排放在C++类所有数据成员的首位,所以C++对象的地址,就是虚函数表指针变量的内存首地址(是指针变量的地址,不是指针变量中的值),所以此处的[ecx]操作,是取出虚函数表指针变量中存放的内容,即虚函数表的首地址。所以edx寄存器中存放的是Contact对象中的虚函数表的首地址。

C++类中如果有虚函数,则该类中会掩藏一个用来存放虚函数表地址的虚函数表指针变量,该虚函数表指针变量放置在该C++对象所有数据成员的首位,所以C++对象的地址就是其虚函数指针变量的内存首地址。

3、汇编代码段3:

.text:005103FF mov ecx, [ebp+var_2AF4]

回到整段汇编代码最开始的地方,[ebp+var_2AF4]中存放的是子类Contact对象的地址。因为下面要调用Contact类对象的虚函数,调用时要将Contact类对象的地址传给被调用的函数,即this指针。C++汇编代码中,是通过ecx寄存器将类对象的地址传给被调用函数,所以此句代码是为下面调用类Contact的虚函数IsLoadFinish做准备的。

在调用C++类的函数时,是通过ecx寄存器传递C++类对象的地址的,即this指针的存放的C++对象的地址。

4、汇编代码段4:

.text:00510405 mov eax, [edx+78h]
.text:00510408 call eax

接着上面,当前edx寄存器中存放的值是虚函数表的首地址(虚函数表指针中存放的值),78h是目标虚函数IsLoadFinish在虚函数中的偏移,所以对edx+78h地址取址,是读出虚函数表该位置存放的就是目标虚函数IsLoadFinish的地址(代码段地址),所以执行这行代码后,eax寄存器中存放的就是目标虚函数IsLoadFinish的地址,然后call eax就是调用虚函数IsLoadFinish了。

在C++中,函数名称其实就是该函数在代码段的首地址,去call这个首地址,就是去调用这个虚函数了。要注意区分数据段地址和代码段的地址。


以上就是关于C++中虚函数调用的实现代码是什么的介绍啦,需要的朋友可以参考上述内容,希望对大家有帮助,欢迎关注群英网络,小编将为大家输出更多高质量的实用文章!

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

标签: 虚函数调用
相关信息推荐
2022-01-20 19:46:48 
摘要:这篇文章我们来了解Java中indexof方法的相关内容,Java中indexof方法有两种使用,下文有详细的介绍,对新手学习和理解indexof方法的使用有帮助,有需要的朋友可以参考,接下来就跟随小编来一起学习一下吧!
2022-05-18 16:59:25 
摘要:使用angular进行开发时,避免不了需要接触生命周期,下面本篇文章就来带大家一起聊聊Angular中的生命周期,希望对大家有所帮助!
2022-02-09 17:56:55 
摘要:这篇文章我们来了解PHP空值是什么。PHP空值就是表示一个变量没有任何的值,用null。那么哪些情况会被赋空值呢?下文有详细的介绍,有需要的朋友可以参考,接下来就跟随小编来一起学习一下吧!
云活动
推荐内容
热门关键词
热门信息
群英网络助力开启安全的云计算之旅
立即注册,领取新人大礼包
  • 联系我们
  • 24小时售后:4006784567
  • 24小时TEL :0668-2555666
  • 售前咨询TEL:400-678-4567

  • 官方微信

    官方微信
Copyright  ©  QY  Network  Company  Ltd. All  Rights  Reserved. 2003-2019  群英网络  版权所有   茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078   粤ICP备09006778号
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
微信公众号
返回顶部
返回顶部 返回顶部