1.有虚函数的类在编译中会加入一个隐藏数据成员,虚表指针。这就使得类的大小比数据成员的大小大4。
2.使用默认构造函数时,构造函数中会出现虚表指针,特征是二次跳转后到跳转表,虚函数表。
CVirtual MyVirtual;
004076D5 8D 4D E8 lea ecx,[MyVirtual]
004076D8 E8 A5 C6 FF FF call CVirtual::CVirtual (0403D82h) ;调用默认构造函数
;虚表
_memmove:
00403D78 E9 C3 BF 08 00 jmp memmove (048FD40h)
charNode::length:
00403D7D E9 7E 5D 01 00 jmp charNode::length (0419B00h)
CVirtual::CVirtual:
00403D82 E9 49 38 00 00 jmp CVirtual::CVirtual (04075D0h) ;跳转
__get_thread_local_invalid_parameter_handler:
00403D87 E9 74 A1 05 00 jmp _get_thread_local_invalid_parameter_handler (045DF00h)
;默认构造函数
CVirtual::CVirtual:
004075D0 55 push ebp
004075D1 8B EC mov ebp,esp
004075D3 81 EC CC 00 00 00 sub esp,0CCh
004075D9 53 push ebx
004075DA 56 push esi
004075DB 57 push edi
004075DC 51 push ecx
004075DD 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
004075E3 B9 33 00 00 00 mov ecx,33h
004075E8 B8 CC CC CC CC mov eax,0CCCCCCCCh
004075ED F3 AB rep stos dword ptr es:[edi]
004075EF 59 pop ecx
004075F0 89 4D F8 mov dword ptr [this],ecx
004075F3 8B 45 F8 mov eax,dword ptr [this]
004075F6 C7 00 54 8E 49 00 mov dword ptr [eax],offset CVirtual::`vftable' (0498E54h)
; 虚表指针
004075FC 8B 45 F8 mov eax,dword ptr [this]
004075FF 5F pop edi
00407600 5E pop esi
00407601 5B pop ebx
00407602 8B E5 mov esp,ebp
00407604 5D pop ebp
00407605 C3 ret
;虚表中位置 004034BD 00402CA2 一次跳转
00498E53 ?? ?? ??
00498E54 BD 34 40 00 A2 mov ebp,0A2004034h
00498E59 2C 40 sub al,40h
00498E5B 00 00
004034BD 二次跳转
@_guard_check_icall_nop@4:
004034B8 E9 93 71 00 00 jmp _guard_check_icall_nop (040A650h)
CVirtual::GetNumber:
004034BD E9 5E 41 00 00 jmp CVirtual::GetNumber (0407620h)
___acrt_show_wide_message_box:
004034C2 E9 29 D2 05 00 jmp __acrt_show_wide_message_box (04606F0h)
00402CA2 二次跳转
__expand_base:
00402C9D E9 DE 61 07 00 jmp _expand_base (0478E80h)
CVirtual::SetNumber:
00402CA2 E9 B9 49 00 00 jmp CVirtual::SetNumber (0407660h)
3.虚函数的识别:
类中隐式定义了一个数据成员
该数据成员在首地址处,占4字节
构造函数会将此数据成员初始化为某个数组的首地址
这个地址属于数据区,是相对固定的地址
在这个数据内,每个元素都是函数指针
他们被调用时,第一个参数必然是this指针
在这些函数内部,很有可能会对this指针使用相对间接的使用方式