オブジェクトの破棄

提供: RAD Studio
移動先: 案内検索

C++ モデルと Object Pascal モデル への移動


オブジェクトの破棄に関わる 2 種類のメカニズムは C++ と Object Pascal で動作が異なります。このメカニズムとは以下のものです。

  • コンストラクタから送出された例外が原因で呼び出されるデストラクタ
  • デストラクタから呼び出される仮想メソッド

Delphi 形式のクラスでは、2 つの言語の方法が組み合わさっています。これについて以下で説明します。

コンストラクタから送出される例外

オブジェクトの構築中に例外が送出された場合、デストラクタの呼び出し方は C++ と Object Pascal で異なります。例として、クラス A から派生したクラス B、そこからさらに派生するクラス C を考えます。

 class   A 
 { 
 
 // body 
 } ; 
 
 class  B:  public   A 
 { 
 
 // body 
 } ; 
 
 class  C:  public   B 
 { 
 
 // body 
 } ;

クラス C のインスタンスの構築中に、クラス B のコンストラクタで例外が発生したとします。C++、Object Pascal、Delphi 形式の各クラスにおける結果を次に説明します。

  • C++ では、まず B のオブジェクト データ メンバのうち構築が完了しているすべてのメンバのデストラクタが呼び出され、次に A のデストラクタが呼び出され、それから A のデータ メンバのうち構築が完了しているすべてのメンバのデストラクタが呼び出されます。ただし、B と C のデストラクタは呼び出されません。
  • Object Pascal では、インスタンスを作成しているクラスのデストラクタだけが自動的に呼び出されます。これは C のデストラクタです。コンストラクタの場合と同様に、継承したデストラクタをデストラクタ中で呼び出すことは、完全にプログラマに任されています。この例で、すべてのデストラクタが継承したデストラクタを呼び出していると仮定すると、C、B、A のデストラクタがこの順序で呼び出されます。さらに、例外が発生する前に B のコンストラクタ内で継承したコンストラクタが既に呼び出されていたかどうかに関係なく、A のデストラクタが呼び出されます。B のデストラクタが継承したデストラクタを呼び出すからです。A のデストラクタが呼び出されるかどうかは、A のコンストラクタが実際に呼び出されたかどうかに依存しません。さらに重要なことに、コンストラクタでは継承したコンストラクタを最初に呼び出すことが一般的なので、C のデストラクタはそのコンストラクタ本体の実行が完了したかどうかに関係なく呼び出されます。
  • Delphi 形式のクラスの場合、VCL、FireMonkey および RTL の基底クラス(Object Pascal で実装)では、Object Pascal の方法でデストラクタを呼び出します。派生クラス(C++ で実装)の方法は、どちらの言語の方法ともまったく同じではありません。この場合はすべてのデストラクタが呼び出されます。ただし、C++ 言語の規則で呼び出されないデストラクタの本体には入りません。

このように、Object Pascal で実装されたクラスでは、デストラクタ本体にユーザーが記述したクリーンアップ コードを処理する機会が提供されています。これには、コンストラクタの例外が発生する前に構築されたサブオブジェクト(オブジェクトであるデータ メンバ)のメモリを解放するコードなどが含まれます。ただし、Delphi 形式のクラスでは、デストラクタが呼び出されたとしても、インスタンスを作成しているクラスや C++ で実装されたその基底クラスに関して、クリーンアップ コードが処理されない可能性があることに注意してください。

デストラクタから呼び出される仮想メソッド

デストラクタにおける仮想メソッドのディスパッチは、コンストラクタの場合と同じパターンで行われます。つまり、Delphi 形式のクラスの場合、派生クラスが最初に破棄されますが、オブジェクトの実行時型は、その後で基底クラスのデストラクタが呼び出されている間も派生クラスの型のままです。そのため、ライブラリの基底クラスのデストラクタで仮想メソッドが呼び出されている場合、既に破棄されたクラスにディスパッチされてしまう可能性があります。

関連項目