jin*_*nge 5 c++ assembly visual-c++ visual-studio-2015
我们知道C++使用vftable来动态决定应该调用哪个虚函数。我想找出当我们调用虚函数时其背后的机制。我已将以下代码编译为程序集。
using namespace std;
class Animal {
int age;
public:
virtual void speak() {}
virtual void wash() {}
};
class Cat : public Animal {
public:
virtual void speak() {}
virtual void wash() {}
};
void main()
{
Animal* animal = new Cat;
animal->speak();
animal->wash();
}
Run Code Online (Sandbox Code Playgroud)
汇编代码非常庞大。我不太明白以下部分。
CONST SEGMENT
??_7Cat@@6B@ DD FLAT:??_R4Cat@@6B@ ; Cat::`vftable'
DD FLAT:?speak@Cat@@UAEXXZ
DD FLAT:?wash@Cat@@UAEXXZ
CONST ENDS
Run Code Online (Sandbox Code Playgroud)
这部分定义了Cat的vftable。但它有三个条目。第一个条目是 RTTI 完整对象定位器。第二个是 Cat::speak。第三个是 Cat::wash。所以我认为 vftable[0] 应该暗示 RTTI 完整对象定位器。但是当我检查main PROC和Cat::Cat PROC中的汇编代码时,to的调用animal->speak()是通过调用vftable[0]实现的,to的调用animal->wash()是通过调用vftable[4]实现的。为什么不是 vftable[4] 和 vftable[8]?
PROC main 和 Cat::Cat 的汇编代码如下所示。
_TEXT SEGMENT
tv75 = -12 ; size = 4
$T1 = -8 ; size = 4
_animal$ = -4 ; size = 4
_main PROC
; 23 : {
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
; 24 : Animal* animal = new Cat;
push 8
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T1[ebp], eax
cmp DWORD PTR $T1[ebp], 0
je SHORT $LN3@main
mov ecx, DWORD PTR $T1[ebp]
call ??0Cat@@QAE@XZ
mov DWORD PTR tv75[ebp], eax
jmp SHORT $LN4@main
$LN3@main:
mov DWORD PTR tv75[ebp], 0
$LN4@main:
mov eax, DWORD PTR tv75[ebp]
mov DWORD PTR _animal$[ebp], eax
; 25 : animal->speak();
mov ecx, DWORD PTR _animal$[ebp]
mov edx, DWORD PTR [ecx]
mov ecx, DWORD PTR _animal$[ebp]
mov eax, DWORD PTR [edx]
call eax
; 26 : animal->wash();
mov ecx, DWORD PTR _animal$[ebp]
mov edx, DWORD PTR [ecx]
mov ecx, DWORD PTR _animal$[ebp]
mov eax, DWORD PTR [edx+4]
call eax
; 27 : }
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; COMDAT ??0Cat@@QAE@XZ
_TEXT SEGMENT
_this$ = -4 ; size = 4
??0Cat@@QAE@XZ PROC ; Cat::Cat, COMDAT
; _this$ = ecx
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov ecx, DWORD PTR _this$[ebp]
call ??0Animal@@QAE@XZ
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], OFFSET ??_7Cat@@6B@
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
??0Cat@@QAE@XZ ENDP ; Cat::Cat
_TEXT ENDS
Run Code Online (Sandbox Code Playgroud)
补充:MSVC编译器x86 19.00.23026
vtable 的布局取决于实现。在您的特定情况下,在编译示例代码时,Microsoft C++ 编译器会生成一个虚函数表,Cat其中speak虚拟函数位于偏移量 0 处,函数wash位于偏移量 4 处。RTTI 信息位于这些函数之前的偏移量 -4 处。
这里的问题是微软的汇编输出是撒谎的。生成的汇编代码将 RTTI 信息放置在偏移量 0 处,并将speak和wash函数放置在偏移量 4 和 8 处。然而,这并不是它在编译器生成的目标文件中的实际布局方式。反汇编目标文件揭示了这个布局:
.new_section .rdata, "dr2"
0000 00 00 00 00 .long ??_R4Cat@@6B@
0004 ??_7Cat@@6B@:
0004 00 00 00 00 .long ?speak@Cat@@UAEXXZ
0008 00 00 00 00 .long ?wash@Cat@@UAEXXZ
Run Code Online (Sandbox Code Playgroud)
不幸的是,Microsoft C/C++ 编译器的汇编输出仅供参考。它不是编译器生成的实际代码的准确完整的表示。特别是它无法可靠地组装到工作对象文件中。
| 归档时间: |
|
| 查看次数: |
2261 次 |
| 最近记录: |