吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1339|回复: 12
收起左侧

[C&C++ 原创] 从汇编角度,理解C++虚函数表

  [复制链接]
水逸寒风 发表于 2024-8-23 09:14
当通过指针访问类的成员函数时:

  • 如果该函数是非虚函数,那么编译器会根据指针的类型找到该函数;也就是说,指针是哪个类的类型就调用哪个类的函数
  • 如果该函数是虚函数,并且派生类有同名的函数遮蔽它,那么编译器会根据指针的指向找到该函数;也就是说,指针指向的对象属于哪个类就调用哪个类的函数。这就是多态。


编译器之所以能通过指针指向的对象找到虚函数,是因为在创建对象时额外地增加了虚函数表。
如果一个类包含了虚函数,那么在创建该类的对象时就会额外地增加一个数组,数组中的每一个元素都是虚函数的入口地址。不过数组和对象是分开存储的,为了将对象和数组关联起来,编译器还要在对象中安插一个指针,指向数组的起始位置。这里的数组就是虚函数表(Virtual function table),简写为vtable。

我们首先从代码角度,来验证一下上面的说明:

我们首先用VC6,写一段代码:
#include <iostream>
using namespace std;

//基类Base
class Base{
public:
        int x;
        void Test()
        {
                cout << "A" << endl;
        }
};

//派生类Derived
class Derived: public Base{
public:
        void Test()
        {
                cout <<"B" <<endl;
        }
};

void Fun(Base *p)
{
        p->Test();        //多态
}

void main()
{
        Base a;
        Derived b;

        Fun(&b);

}
输出结果为:
B
如果将main函数中的Fun(&b);修改为Fun(&a);,那输出结果为:
A
因此验证了如果该函数是虚函数,并且派生类有同名的函数遮蔽它,那么编译器会根据指针的指向找到该函数;也就是说,指针指向的对象属于哪个类就调用哪个类的函数

当我们去掉Base类中virtual void Test()的virtual,修改为void Test(),那无论传入的是&b还是&a,那输出结果都是
A
因此验证了如果该函数是非虚函数,那么编译器会根据指针的类型找到该函数;也就是说,指针是哪个类的类型就调用哪个类的函数


现在我们分析下虚函数表:如果一个类包含了虚函数,那么在创建该类的对象时就会额外地增加一个数组,数组中的每一个元素都是虚函数的入口地址。不过数组和对象是分开存储的,为了将对象和数组关联起来,编译器还要在对象中安插一个指针,指向数组的起始位置。这里的数组就是虚函数表(Virtual function table),简写为vtable。

首先看一下,非虚函数和虚函数的调用在汇编角度有什么区别。
还是去掉Base类中virtual void Test()的virtual,修改为void Test(),然后跟进汇编:
25:       p->Test();  //多态
00401188   mov         ecx,dword ptr [ebp+8]
0040118B   call        @ILT+75(Base::Test) (00401050)        //在程序编译后,即将地址固化为00401050,即为Base::Test。
我们可以看到,在非虚函数的情况下,函数的调用是编译时绑定(编译时绑定是指在程序执行之前,由编译器和连接器确定方法调用的目标),也就是直接调用。

如果还原为以上的示例代码,即为原来的虚函数模式,然后跟进汇编:
25:       p->Test();  //多态
004011A8   mov         eax,dword ptr [ebp+8]
004011AB   mov         edx,dword ptr [eax]
004011AD   mov         esi,esp
004011AF   mov         ecx,dword ptr [ebp+8]
004011B2   call        dword ptr [edx]        //程序编译后,地址为dword ptr [edx],[edx]是一个可变变量,即地址非固定。
我们可以看到,在虚函数的情况下,函数的调用是运行时绑定(运行时绑定是指在程序运行时,根据对象的实际类型确定方法调用的目标。运行时绑定也叫作动态绑定或后期绑定),也就是简间接调用。

这就是为什么虚函数能够调用派生类函数的在汇编层面的原因:虚函数是运行时绑定(也叫动态绑定或后期绑定)。

那我们继续分析上面的反汇编代码:
004011A8   mov         eax,dword ptr [ebp+8]        //使用[ebp+8]指定栈中存储的第1个参数,并将其读出到 eax 寄存器中。第一个参数为b对象的地址。
004011AB   mov         edx,dword ptr [eax]             //将b对象的地址的第一个双字节的数值,读出到edx。这个数值就是虚函数表的的地址,
004011B2   call           dword ptr [edx]                   //调用函数,函数地址即为虚函数表的第一个双字节值。

b对象的内存模型中,开始的第一个dword的值为虚函数表的地址;因为就一个虚函数,所以虚函数表的第一个dword的值就是这次调用的函数地址。

免费评分

参与人数 3吾爱币 +8 热心值 +3 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
laozhang4201 + 1 + 1 我很赞同!
solitary369 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

durongze 发表于 2024-11-26 14:43

大佬可以帮我看一下吗?
链接: https://pan.baidu.com/s/1AHPhQ-33o3mtCbFPH418WA?pwd=4hrf 提取码: 4hrf
报错的工程是 test_3rdlib_app 和 test_3rdlib_shared
-------------------------------------------------------------------------------------------
  test_3rdlib_app 依赖 动态库 test_3rdlib_shared
--------------------------------------------------------------------------------------------
3:58:41:318        1>MSVCRTD.lib(init.obj) : error LNK2019: unresolved external symbol _CrtDbgReport referenced in function _CRT_RTC_INIT
13:58:41:318        1>MSVCRTD.lib(init.obj) : error LNK2019: unresolved external symbol _CrtDbgReportW referenced in function _CRT_RTC_INITW
13:58:41:318        1>MSVCRTD.lib(error.obj) : error LNK2019: unresolved external symbol strcpy_s referenced in function "void __cdecl _RTC_StackFailure(void *,char const *)" (?_RTC_StackFailure@@YAXPEAXPEBD@Z)
13:58:41:318        1>MSVCRTD.lib(error.obj) : error LNK2019: unresolved external symbol strcat_s referenced in function "void __cdecl _RTC_StackFailure(void *,char const *)" (?_RTC_StackFailure@@YAXPEAXPEBD@Z)
13:58:41:318        1>MSVCRTD.lib(error.obj) : error LNK2019: unresolved external symbol __stdio_common_vsprintf_s referenced in function _vsprintf_s_l
13:58:41:318        1>MSVCRTD.lib(error.obj) : error LNK2001: unresolved external symbol __C_specific_handler_noexcept
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol _wmakepath_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol _wsplitpath_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol wcscpy_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol __vcrt_GetModuleFileNameW referenced in function "struct HINSTANCE__ * __cdecl GetPdbDll(void)" (?GetPdbDll@@YAPEAUHINSTANCE__@@XZ)
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol __vcrt_GetModuleHandleW referenced in function "struct HINSTANCE__ * __cdecl GetPdbDll(void)" (?GetPdbDll@@YAPEAUHINSTANCE__@@XZ)
13:58:41:318        1>MSVCRTD.lib(pdblkup.obj) : error LNK2019: unresolved external symbol __vcrt_LoadLibraryExW referenced in function "struct HINSTANCE__ * __cdecl GetPdbDll(void)" (?GetPdbDll@@YAPEAUHINSTANCE__@@XZ)
13:58:41:318        1>BuildSrc\Debug\test_3rdlib.dll : fatal error LNK1120: 17 unresolved externals
--------------------------------
gusong125 发表于 2024-8-23 21:14
xixicoco 发表于 2024-8-24 00:03
Carlson20 发表于 2024-8-25 19:38
哭了,还没学过汇编,我补补课
fhlfxtd 发表于 2024-8-25 20:26
感谢分享
AuroraVerses 发表于 2024-8-27 22:34
感谢大佬分享
15090878185 发表于 2024-8-30 11:19
感谢大佬分享
SOV710 发表于 2024-9-1 16:23
感谢大佬分享
aiguohou 发表于 2024-9-1 17:41
学习了,感谢
haoxia57 发表于 2024-9-23 15:38
感谢大佬分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-15 20:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表