C++ではvftableを使用して、どの仮想関数を呼び出すべきかを動的に決定することが知られています。私は仮想関数を呼び出すとき、その背後にあるメカニズムを見つけたいと思います。私はアセンブリに次のコードをコンパイルしました。vftable [0]は、最初の仮想関数またはRTTI Complete Object Locatorを格納していますか?
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();
}
アセンブリコードは大量です。私は次の部分をかなり理解していません。
CONST SEGMENT
[email protected]@[email protected] DD FLAT:[email protected]@[email protected] ; Cat::`vftable'
DD FLAT:[email protected]@@UAEXXZ
DD FLAT:[email protected]@@UAEXXZ
CONST ENDS
この部分は、Catのvftableを定義します。しかし、それは3つのエントリがあります。最初のエントリはRTTI Complete Object Locatorです。 2番目はCat :: speakです。 3番目はCat :: washです。だから、vftable [0]はRTTI Complete Object Locatorを暗示すべきだと思います。しかし、メインのPROCとCat :: Cat PROCのアセンブリコードをチェックすると、animal->speak()
への呼び出しはvftable [0]を呼び出して実装され、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 [email protected]@Z ; operator new
add esp, 4
mov DWORD PTR $T1[ebp], eax
cmp DWORD PTR $T1[ebp], 0
je SHORT [email protected]
mov ecx, DWORD PTR $T1[ebp]
call [email protected]@[email protected]
mov DWORD PTR tv75[ebp], eax
jmp SHORT [email protected]
[email protected]:
mov DWORD PTR tv75[ebp], 0
[email protected]:
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 [email protected]@[email protected]
_TEXT SEGMENT
_this$ = -4 ; size = 4
[email protected]@[email protected] 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 [email protected]@[email protected]
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], OFFSET [email protected]@[email protected]
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
[email protected]@[email protected] ENDP ; Cat::Cat
_TEXT ENDS
補足:MSVCコンパイラのx86 19.00.23026
最後に私が確認したところでは、MSVCは状況に応じて異なるvtableレイアウトを生成します。チェックアウトしたい場合があります –