2

この質問-ほとんどの派生クラスのデストラクタでの純粋な仮想呼び出し-に続いて、いくつかのコードを試していくつかの構文をチェックし、連続するデストラクタが呼び出されると、それらが関連する仮想関数を呼び出すことを発見しました。次のコードを検討してください。

class Base
{
public:
virtual void Method() = 0;
};

class Derived :  public Base
{
public:
~Derived()
{
    Method();
}

virtual void Method()
{
    cout << "D";
}
};

class DoubleD : public Derived
{
public:

~DoubleD()
{
    Method();
}

virtual void Method()
{
    cout << "DD";
}
};

int main(array<System::String ^> ^args)
{
    DoubleD D;
    DoubleD E;
    return 0;
}

予想どおり、オブジェクトが破棄されると、正しいメソッドが呼び出されます (たとえば、最初に最も派生したもの、次に 2 番目に派生したもの)。

出力: DD D

私の質問は、なぜこれが機能するのですか? c'tor/d'tor で仮想関数を呼び出すことを意図していないため、仮想テーブルが正しく「巻き戻される」のはなぜですか。

たとえば、最も派生したものが機能する理由がわかります。これは、これが開始されたときの仮想関数ポインター テーブルの状態でした。しかし、Derivedのデストラクタが呼び出されたときに、そのクラスの の実装を指すようにテーブルが正しく設定されるのはなぜですかMethod

そのままにしておくのはどうでしょうか、それが良い場合は、値を NULL に設定してください。

4

4 に答える 4

6

c'tor/d'tor で仮想関数を呼び出すことを意図していないため、仮想テーブルが正しく「巻き戻される」のはなぜですか。

前提が間違っています。仮想関数がどのように機能するかを知っていれば、コンストラクターまたはデストラクターから仮想関数を呼び出すことに何の問題もありません。これまで見てきたように、動的型は実行中のコンストラクタまたはデストラクタの型であるため、まだ構築されていない、または既に破棄されているオブジェクトの部分への仮想呼び出しは取得されません。

于 2013-05-13T20:59:39.193 に答える
3

動作は完全に明確に定義されています。コンパイラ ベンダーがどのようにそれを実装したかについて心配する必要はありません (ただし、自分で推論したり調べたりするのはそれほど難しくありません)。

直感的でない動作のため、デストラクタで仮想関数を呼び出すことは一般的にお勧めしませんが、根本的に問題はありません。

于 2013-05-13T20:46:22.847 に答える
1

オブジェクト作成時の初期設定後、実行時に仮想テーブルが変更されません。一部の実装では、仮想テーブルはクラスごとに作成されます。

あなたの例では、DoubleD オブジェクトが破棄されると、DoubleD クラスのメソッド関数が呼び出されます。これは、オブジェクトの DoubleD 部分がまだ完全に破棄されていないためです。DoubleD クラスの VTable には、(継承の最後のレベルで) オーバーライドされるため、そのクラスのメソッドを指すメソッド関数のエントリがあります。

DoubleD が破棄されると、オブジェクト型は Derived 型になります。そのため、呼び出しは Derived クラスの vtable 内のメソッドに移動する必要があります。したがって、動作。

于 2013-05-13T21:03:12.333 に答える