継承と仮想デストラクタ

提供: C++入門
移動: 案内検索
スポンサーリンク

クラスを継承するときに、基底クラスが仮想デストラクタ(デストラクタ仮想関数ではない)を持たない場合、継承してはいけません。

読み方

仮想デストラクタ
かそう ですとらくた
継承
けいしょう

概要

継承とコンストラクタとデストラクタコンストラクタとデストラクタが呼び出される順番について書きました。

仮想関数でも書きましたが、サブクラスのインスタンスを基底クラスのポインタで扱ったときに、デストラクタの呼び出しに特徴があります。 デストラクタの書き方について、このページで補足します。

サブクラスのデストラクタが呼び出されない例

ソースコード deprecation_extends_1.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void x() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B *p = new C1();
 
        p->f();
 
        delete p;
 
        return 0;
}

コンパイル

g++  deprecation_extends_1.cpp -o deprecation_extends_1

実行例

C1のインスタンスを基底クラス B のポインタで受け取って、解放した場合、基底クラスのデストラクタは呼び出されていますが、サブクラス C1 のデストラクタが呼び出されませんでした。 もし、サブクラスの中でリソースの解放などの必須処理が記述されている場合、実行されなくなり、システムへ影響を与える問題に発展します。

% ./deprecation_extends_1
B::B()
C1::C1()
void B::f()
B::~B()

次の項目で、サブクラスのデストラクタが呼び出されない問題を解決します。

サブクラスのデストラクタの問題を解決する例

deprecation_extends_1.cpp の例との違いは、基底クラス のデストラクタに virtual が指定されていることです。 デストラクタに virtual を指定することにより、インスタンスが解放されるときに、基底クラスとサブクラスの両方のデストラクタが呼び出されます。

ソースコード virtual_destructor_2.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                virtual ~B() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void x() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                ~C1() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B *p = new C1();
 
        p->f();
 
        delete p;
 
        return 0;
}

コンパイル

g++  virtual_destructor_2.cpp -o virtual_destructor_2

実行例

基底クラスとサブクラスの両方のデストラクタが呼び出されるようになりました。

% ./virtual_destructor_2
B::B()
C1::C1()
void B::f()
virtual C1::~C1()
virtual B::~B()

仮想デストラクタが持たないクラスを継承してはいけない

どんなクラスでもデストラクタを仮想デストラクタ(virtualデストラクタ)にしないといけない、というわけではありません。

クラスがvirtualをもった場合、仮想関数テーブルが作成されます。仮想関数テーブルを持つとオブジェクトサイズが大きくなってしまいます。

関連項目




スポンサーリンク