3

次の例を考えます。

class BaseClass
{
  BaseClass()
  {
  };

  virtual ~BaseClass()
  {
    this->Cleanup();
  };

  virtual void Cleanup()
  {
    // Do cleanup here.
  };
};

class Level1DerivedClass : public BaseClass
{
  Level1DerivedClass()
  {
  };

  virtual ~Level1DerivedClass()
  {
  };

  virtual void Cleanup()
  {
    // Call my base cleanup.
    BaseClass::Cleanup();

    // Do additional cleanup here.
  };
};

class Level2DerivedClass : public Level1DerivedClass
{
  Level2DerivedClass()
  {
  };

  ~Level2DerivedClass()
  {
  };

  void Cleanup()
  {
    // Call my base cleanup.
    Level1DerivedClass::Cleanup();

    // Do additional cleanup here.
  };  
};


main()
{
  Level2DerivedClass * derived2 = new Level2DerivedClass();
  delete derived2;
  return 0;
}

派生クラス参照を削除するとフローは次のようになると予想されます。

  1. Level2DerivedClassデストラクタが実行されます。
  2. Level1DerivedClassデストラクタは仮想であるため、実行されます。
  3. BaseClassデストラクタは仮想であるため、実行されます。
  4. BaseClass ::CleanupLevel1DerivedClass::Cleanupはどちらも仮想であるため、 BaseClassデストラクタのBaseClass'this 'ポインタからの呼び出しは、最も派生したクラスであるLevel2DerivedClass::Cleanupの実装を実行します。
  5. Level2DerivedClass :: Cleanupは、その親のCleanup実装を呼び出します。
  6. Level1DerivedClass :: Cleanupは、その親のCleanup実装を呼び出します。

何が起こっているのかというと、私が期待している方法よりも、継承の各レベル(1〜3)のデストラクタを呼び出しているということです。ただし、this-> Cleanup()BaseClassデストラクタから呼び出されると、独自の実装のみが実行されます。通常、派生クラスポインターをインスタンス化し、それを基本クラスポインターとしてキャストし、基本クラスポインター(この場合は「this」)から仮想メソッドを呼び出すと、これが発生する理由がわかりません。派生クラスの実装(「仮想」の要点ですよね?)。私の例では、Level2DerivedClass::CleanupLevel1DerivedClass::Cleanupが呼び出されることはありません。

このように設定する理由は、オブジェクトを破棄せずにクリーンアップコードを呼び出せるようにするためです。そのため、実際のデストラクタ本体からオブジェクトを抽象化しています。

これを行うためのより適切な方法についての提案があれば、私はすべての耳です。しかし、セットアップが機能しない理由についても説明したいと思います。何を誤解しているのでしょうか。

よろしくお願いします。

4

2 に答える 2

7

経験則は次のとおりです。構築中または破壊中に仮想関数を呼び出さないでください

彼らはあなたが期待するように振る舞いません。各デストラクタが終了すると、の動的タイプthisが効果的に変更されます。C ++標準の[class.cdtor]から:

仮想関数がコンストラクター(非静的データメンバーの場合はmem-initializerまたはbrace-or-equal-initializerを含む)またはデストラクタから直接または間接的に呼び出され、呼び出しが適用されるオブジェクトがオブジェクトである場合構築中または破棄中、呼び出される関数は、コンストラクタまたはデストラクタ自体のクラスまたはそのベースの1つで定義されたものですが、コンストラクタまたはデストラクタのクラスから派生したクラスでオーバーライドしたり、いずれかのクラスでオーバーライドしたりする関数ではありません。最も派生したオブジェクトの他の基本クラス。

于 2012-05-30T17:10:46.517 に答える
2

物事を行うための適切な方法は次のとおりです。デストラクタで、自分自身の後で、自分だけをきれいにします。あなたの子供やあなたの両親の後に掃除しないでください。

デストラクタからではなく物事をクリーンアップしたい場合、あなたはそれを間違っています。C ++には、RAIIと呼ばれる小さなものがあります。リソースの取得は初期化です。しかし、正式な名前が付いていないように見えるデュアルもありますが、これが機能する可能性があります:RDID、Resource DisposalIsDestruction。

もちろん、RAII / RDIDの哲学に従う必要はありませんが、それはC++の方法ではありません。

于 2012-05-30T17:32:56.273 に答える