vftable[0]是存储第一个虚拟函数还是RTTI完整对象定位器? 我们知道,C++使用一个VFABLE来动态地决定应该调用哪个虚拟函数。当我们调用虚函数时,我想找出它背后的机制。我已将以下代码编译成汇编 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(); }

vftable[0]是存储第一个虚拟函数还是RTTI完整对象定位器? 我们知道,C++使用一个VFABLE来动态地决定应该调用哪个虚拟函数。当我们调用虚函数时,我想找出它背后的机制。我已将以下代码编译成汇编 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(); },c++,assembly,visual-c++,visual-studio-2015,C++,Assembly,Visual C++,Visual Studio 2015,汇编代码非常庞大。我不太明白下面的部分 CONST SEGMENT ??_7Cat@@6B@ DD FLAT:??_R4Cat@@6B@ ; Cat::`vftable' DD FLAT:?speak@Cat@@UAEXXZ DD FLAT:?wash@Cat@@UAEXXZ CONST ENDS 本部分定义了Cat的vftable。但它有三个条目。第一个条目是RTTI完整对象定位器。第二个是猫:说话。第三种是洗猫。所以我认为vftable[0]应

汇编代码非常庞大。我不太明白下面的部分

CONST   SEGMENT
??_7Cat@@6B@ DD FLAT:??_R4Cat@@6B@          ; Cat::`vftable'
    DD  FLAT:?speak@Cat@@UAEXXZ
    DD  FLAT:?wash@Cat@@UAEXXZ
CONST   ENDS
本部分定义了Cat的vftable。但它有三个条目。第一个条目是RTTI完整对象定位器。第二个是猫:说话。第三种是洗猫。所以我认为vftable[0]应该暗示RTTI完整的对象定位器。但是当我检查main PROC和Cat::Cat PROC中的汇编代码时,调用vftable[0]实现对
animal->speak()
的调用,调用vftable[4]实现对
animal->wash()的调用。为什么不是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

补充:MSVC编译器x86 19.00.23026

vtables的布局取决于实现。在您的特定情况下,编译示例代码时,微软C++编译器生成一个VTABLE,用于<代码> CAT/COD> >其中<代码> Tale>代码>虚函数位于偏移0,而 WASE/COD>函数在偏移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

不幸的是,微软的C/C++编译器的汇编输出只是为了提供信息。它不是编译器生成的实际代码的准确和完整的表示。特别是,它不能可靠地组装到工作对象文件中。

上次我检查时,MSVC会根据情况生成不同的vtable布局,您可能需要检查它