2007年09月19日

仮想デストラクタの持つ意味

以下のコードは危険である。

#include<iostream>
using namespace std;

class Base{
  public:
  Base(){cout << "Base::Constructor" << endl;}
  ~Base(){cout << "Base::Destructor" << endl;}
};

class Derived:public Base{
  public:
  Derived(){cout << "Derived::Constructor" << endl;}
  ~Derived(){cout << "Derived::Destructor" << endl;}
};

int main(void)
{
  Base *p = new Derived();
  delete p;
}

実行結果

Base::Constructor
Derived::Constructor
Base::Destructor

基底クラスのオブジェクトとして派生クラスのオブジェクトを生成し代入しているが、delete時に派生クラスのオブジェクトのデストラクタの呼び出しが行われないのである。仮に派生クラスのメンバ変数にポインタ等が含まれており、デストラクタでそのポインタのメモリを開放する記述を行っていた場合、デストラクタが呼び出されずにメモリリークの原因となってしまう。その為、継承の可能性のあるクラス(事実上ほぼ全て)のデストラクタは仮想デストラクタにしておく必要がある。そうすることによって、派生クラスのデストラクタが呼び出され、さらに基底クラスのデストラクタの呼び出しも正常に行われるのである。

#include<iostream>
using namespace std;

class Base{
  public:
  Base(){cout << "Base::Constructor" << endl;}
  virtual ~Base(){cout << "Base::Destructor" << endl;}
};

class Derived:public Base{
  public:
  Derived(){cout << "Derived::Constructor" << endl;}
  virtual ~Derived(){cout << "Derived::Destructor" << endl;}
};

int main(void)
{
  Base *p = new Derived();
  delete p;
}

実行結果

Base::Constructor
Derived::Constructor
Derived::Destructor
Base::Destructor

ちなみに「デストラクタは仮想デストラクタにしておく必要がある」とあるが、逆に継承を望んでいるクラスのデストラクタが仮想デストラクタで無い場合は、そのクラスが継承を想定していない(継承に対しての対策が無い)設計だという可能性も考える必要がある。
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック