デストラクター

C++デストラクター は、オブジェクトを破棄するときに、メモリーの割り振り解除、およびクラス・オブジェクトとそのクラス・メンバーに関するその他の終結処理を行うために使用されます。 オブジェクトがスコープを超えるか、明示的にオブジェクトを削除するときに、 そのクラス・オブジェクトのデストラクターを呼び出します。

デストラクターは、そのクラスと同じ名前を持つメンバー関数で、 接頭部として ~ (波形記号) が付きます。 次に例を示します。

class X {
public:
  // Constructor for class X
  X();
  // Destructor for class X
  ~X();
};

デストラクターは、引き数を使用せず、戻りの型もありません。 そのアドレスを取得することはできません。 デストラクターを constvolatileconst volatile、 または static で宣言できません。 デストラクターは、virtual、または純粋 virtual で宣言できます。

あるクラスにユーザー定義のデストラクターが必要であるが、 それが存在しない場合、コンパイラーは、デストラクターを暗黙的に宣言します。 この暗黙的に宣言されたデストラクターは、そのクラスのインライン・パブリック・メンバーです。

コンパイラーがデストラクターを使用して、デストラクターのクラス型のオブジェクトを破棄する場合、 コンパイラーは、暗黙的に宣言されたデストラクターを暗黙的に定義します。 クラス A が、暗黙的に宣言されたデストラクターを持っているとします。 次は、コンパイラーが A に対して暗黙的に定義を行う関数と同等です。

  A::~A() { }

コンパイラーは、まず最初に暗黙的に宣言された基底クラスのデストラクターと、 クラス A の非静的データ・メンバーを暗黙的に定義してから、 暗黙的に宣言された A のデストラクターを定義します。

クラス A のデストラクターは、次のことがすべて true であれば 単純 です。

上記のいずれかが false であれば、デストラクターは非単純 です。

共用体メンバーは、非単純デストラクターを持つクラス型にはできません。

クラス型であるクラス・メンバーは、独自のデストラクターを持つことができます。 基底クラスと派生クラスは、両方ともデストラクターを持つことができますが、デストラクターは継承されません。 基底クラス A または A のメンバーがデストラクターを持っており、A から派生したクラスがデストラクターを宣言しない場合、デフォルトのデストラクターが生成されます。

デフォルト・デストラクターは、基底クラスおよび派生クラスのメンバーのデストラクターを 呼び出します。

基底クラスおよびメンバーのデストラクターは、それらのコンストラクターが完了する順番の逆順で呼び出されます。

  1. クラス・オブジェクト用のデストラクターは、メンバーおよび基底用の デストラクターより前に呼び出されます。
  2. 非静的メンバーのデストラクターは、仮想基底クラスのデストラクターより前に、呼び出されます。
  3. 非仮想基底クラスのデストラクターは、仮想基底クラスのデストラクターより 前に、呼び出されます。

デストラクターを持つクラス・オブジェクトの例外をスロー (throw) すると、 プログラムが catch ブロックの外に制御を渡すまで、 スローする一時オブジェクトのデストラクターを呼び出しません。

自動オブジェクト (auto または register を宣言されたローカル・オブジェクト、 あるいは static または extern として宣言されていないローカル・オブジェクト) または一時オブジェクトがスコープ外に渡されると、デストラクターが暗黙的に呼び出されます。 構築された外部オブジェクトや静的オブジェクトのプログラム終了処理時にも、 暗黙的にデストラクターを呼び出します。 デストラクターは、new 演算子によって作成されたオブジェクト に対してユーザーが delete 演算子を使用すると呼び出されます。

次に例を示します。

#include <string>
 
class Y {
private:
  char * string;
  int number;
public:
  // Constructor
  Y(const char*, int);
  // Destructor
  ~Y() { delete[] string; }
};
 
// Define class Y constructor
Y::Y(const char* n, int a) {
  string = strcpy(new char[strlen(n) + 1 ], n);
  number = a;
}
 
int main () {
  // Create and initialize
  // object of class Y
  Y yobj = Y("somestring", 10);
 
  // ...
 
  // Destructor ~Y is called before
  // control returns from main()
}

デストラクターを明示的に使用してオブジェクトを破棄することもできますが、 この方法はお勧めできません。 しかし、配置 new 演算子を使用して作成されたオブジェクトを破棄するために、 オブジェクトのデストラクターを明示的に呼び出すことができます。 次の例は、このことを示しています。

#include <new>
#include <iostream>
using namespace std;
class A {
  public:
    A() { cout << "A::A()" << endl; }
    ~A() { cout << "A::~A()" << endl; }
};
int main () {
  char* p = new char[sizeof(A)];
  A* ap = new (p) A;
  ap->A::~A();
  delete [] p;
}

ステートメント A* ap = new (p) A は、型 A の新規のオブジェクトを、フリー・ストアにではなく、p が割り振ったメモリーに動的に作成します。 ステートメント delete [] p は、p が割り振ったストレージを削除します。 しかし、ランタイムは、A のデストラクターを明示的に呼び出すまでは (ステートメント ap->A::~A() と指定して)、ap が指すオブジェクトはまだ存在していると判断しています。

非クラス型は、疑似デストラクター を持っています。 次の例は、整数型の疑似デストラクターを呼び出します。

typedef int I;
int main() {
  I x = 10;
  x.I::~I();
  x = 20;
}

疑似デストラクターの呼び出し x.I::~I() は、まったく効果はありません。 オブジェクト x は、破棄されておらず、代入 x = 20 はまだ有効です。 疑似デストラクターは、 非クラス型を有効にするためにデストラクターを明示的に呼び出すための構文を必要とするので、 任意の型に対するデストラクターが存在するかどうかを把握しなくても、コードの書き込みができます。

関連参照

IBM Copyright 2003