10

この記事を見ていたら、「基本クラスのデストラクタに入ると、オブジェクトは基本クラスのオブジェクトになり、仮想関数、dynamic_castsなどのC++のすべての部分がそのように処理されます」と書かれています。これは、破壊中にvptrが変更されたことを意味しますか?それはどのように起こりますか?

4

2 に答える 2

12

仮想関数テーブルを使用するすべての実装(つまり、現在のすべてのC ++実装)では、答えは「はい」vptrです。実行されているデストラクタのタイプの変更です。その理由は、標準では、破棄されるオブジェクトのタイプが実行されるデストラクタのタイプであることが要求れているためです。

3つのタイプB、D、MD(ベース、派生、最も派生)の階層があり、タイプのオブジェクトをインスタンス化して破棄する場合、オブジェクトのタイプのMD実行中MD::~MD()です MDが、ベースデストラクタへの暗黙の呼び出しの場合が実行される場合、オブジェクトの実行時型はである必要があります D。これは、を更新することで実現されvptrます。

于 2011-10-27T13:55:32.210 に答える
6

もちろん、衒学的な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();
}

ここで、Polydtorの開始中括弧にブレークポイントを設定します。

このコードを開始中括弧で中断して実行すると(コンストラクターの本体が実行を開始する直前)、vptrを確認できます。ここに画像の説明を入力してください

また、Polydtorの逆アセンブリを表示できます。

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_、デバッガでランタイムアサーションエラーが生成されます。

于 2011-10-27T14:06:28.017 に答える