この記事を見ていたら、「基本クラスのデストラクタに入ると、オブジェクトは基本クラスのオブジェクトになり、仮想関数、dynamic_castsなどのC++のすべての部分がそのように処理されます」と書かれています。これは、破壊中にvptrが変更されたことを意味しますか?それはどのように起こりますか?
2 に答える
仮想関数テーブルを使用するすべての実装(つまり、現在のすべてのC ++実装)では、答えは「はい」vptr
です。実行されているデストラクタのタイプの変更です。その理由は、標準では、破棄されるオブジェクトのタイプが実行されるデストラクタのタイプであることが要求されているためです。
3つのタイプB、D、MD(ベース、派生、最も派生)の階層があり、タイプのオブジェクトをインスタンス化して破棄する場合、オブジェクトのタイプのMD
実行中MD::~MD()
はです MD
が、ベースデストラクタへの暗黙の呼び出しの場合が実行される場合、オブジェクトの実行時型はである必要があります D
。これは、を更新することで実現されvptr
ます。
もちろん、衒学的なC ++の答えは、「標準では、vtblsやポリモーフィズムの実装方法については何も述べていません」です。
しかし、実際にはそうです。vtblは、基本クラスのデストラクタの本体が実行を開始する前に変更されます。
編集:
これが、MSVC10を使用してこれが自分で発生することを確認する方法です。まず、テストコード:
#include <string>
#include <iostream>
using namespace std;
class Poly
{
public:
virtual ~Poly();
virtual void Foo() const = 0;
virtual void Test() const = 0 { cout << "PolyTest\n"; }
};
class Left : public Poly
{
public:
~Left()
{
cout << "~Left\n";
}
virtual void Foo() const { cout << "Left\n"; }
virtual void Test() const { cout << "LeftTest\n"; }
};
class Right : public Poly
{
public:
~Right() { cout << "~Right\n"; }
virtual void Foo() const { cout << "Right\n"; }
virtual void Test() const { cout << "RightTest\n"; }
};
void DoTest(const Poly& poly)
{
poly.Test();
}
Poly::~Poly()
{ // <=== BKPT HERE
DoTest(*this);
cout << "~Poly\n";
}
void DoIt()
{
Poly* poly = new Left;
cout << "Constructed...\n";
poly->Test();
delete poly;
cout << "Destroyed...\n";
}
int main()
{
DoIt();
}
ここで、Poly
dtorの開始中括弧にブレークポイントを設定します。
このコードを開始中括弧で中断して実行すると(コンストラクターの本体が実行を開始する直前)、vptrを確認できます。
また、Poly
dtorの逆アセンブリを表示できます。
Poly::~Poly()
{
000000013FE33CF0 mov qword ptr [rsp+8],rcx
000000013FE33CF5 push rdi
000000013FE33CF6 sub rsp,20h
000000013FE33CFA mov rdi,rsp
000000013FE33CFD mov ecx,8
000000013FE33D02 mov eax,0CCCCCCCCh
000000013FE33D07 rep stos dword ptr [rdi]
000000013FE33D09 mov rcx,qword ptr [rsp+30h]
000000013FE33D0E mov rax,qword ptr [this]
000000013FE33D13 lea rcx,[Poly::`vftable' (13FE378B0h)]
000000013FE33D1A mov qword ptr [rax],rcx
DoTest(*this);
000000013FE33D1D mov rcx,qword ptr [this]
000000013FE33D22 call DoTest (13FE31073h)
cout << "~Poly\n";
000000013FE33D27 lea rdx,[std::_Iosb<int>::end+4 (13FE37888h)]
000000013FE33D2E mov rcx,qword ptr [__imp_std::cout (13FE3C590h)]
000000013FE33D35 call std::operator<<<std::char_traits<char> > (13FE3104Bh)
}
000000013FE33D3A add rsp,20h
000000013FE33D3E pop rdi
000000013FE33D3F ret
次の行をステップオーバーして、デストラクタの本体に入り、vptrをもう一度覗き見します。
デストラクタの本体内から呼び出すと
DoTest
、vtblはすでにを指すように変更されておりpurecall_
、デバッガでランタイムアサーションエラーが生成されます。