派生クラスのコンストラクタ・デストラクタ

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

コンストラクタ・デストラクタの呼び出し順

スポンサーリンク

派生クラスのコンストラクタ、デストラクタがどのように呼び出されるか確認します。

#include <iostream>
 
class base {
public:
    // constructor
    base() {
        std::cout << "基底クラスのコンストラクタ" << "\n";
    }
    // destructor
    ~base() {
        std::cout << "基底クラスのデストラクタ" << "\n";
    }
};
 
class deriv : public base {
public:
    // constructor
    deriv() {
        std::cout << "派生クラスのコンストラクタ" << "\n";
    }
    // destructor
    ~deriv() {
        std::cout << "派生クラスのデストラクタ" << "\n";
    }
};
 
int main() {
    deriv *test_ptr = new deriv;
    // 基底クラスのコンストラクタ
    // 派生クラスのコンストラクタ ...と出力される
 
    std::cout << "\n";
 
    delete test_ptr;
    test_ptr = NULL;
    // 派生クラスのデストラクタ
    // 基底クラスのデストラクタ ...と出力される
 
    std::cout << "\n\n";
 
    // 通常の変数宣言でインスタンス化
    deriv sample;
 
    return 0;
}

実行結果。

基底クラスのコンストラクタ
派生クラスのコンストラクタ
 
派生クラスのデストラクタ
基底クラスのデストラクタ
 
基底クラスのコンストラクタ
派生クラスのコンストラクタ
派生クラスのデストラクタ
基底クラスのデストラクタ

派生クラスをインスタンス化した場合、コンストラクタは・・・

1.基底クラスのコンストラクタ
2.派生クラスのコンストラクタ

の順で呼び出されることを確認できます。
デストラクタは・・・

1.派生クラスのデストラクタ
2.基底クラスのデストラクタ

の順で呼び出されています。

基底クラスの型で、派生クラスをインスタンス化

次に、基底クラスの型で、派生クラスのオブジェクトを扱ってみます。
同じクラスを用いて、メインルーチンを書き換えます。

#include <iostream>
 
class base {
public:
    // constructor
    base() {
        std::cout << "基底クラスのコンストラクタ" << "\n";
    }
    // destructor
    ~base() {
        std::cout << "基底クラスのデストラクタ" << "\n";
    }
};
 
class deriv : public base {
public:
    // constructor
    deriv() {
        std::cout << "派生クラスのコンストラクタ" << "\n";
    }
    // destructor
    ~deriv() {
        std::cout << "派生クラスのデストラクタ" << "\n";
    }
};
 
int main() {
    base *test_ptr = new deriv;
    // 基底クラスのコンストラクタ
    // 派生クラスのコンストラクタ ...と出力される
 
    std::cout << "\n";
 
    delete test_ptr;
    test_ptr = NULL;
    // 派生クラスのデストラクタ ...だけが出力される
 
    return 0;
}

実行結果。

基底クラスのコンストラクタ
派生クラスのコンストラクタ
 
基底クラスのデストラクタ

これは、問題のある動作となっています。
派生クラスのインスタンス化を行ったにもかかわらず、派生クラスのデストラクタが呼び出されていません。
test_ptrが、基底クラス(base)クラスへのポインタとなっているために、「delete test_ptr」では基底クラスしか認識しないためです。
そのため、このようなメインルーチンを書く場合は、基底クラスのデストラクタを仮想関数とする必要があります。

#include <iostream>
 
class base {
public:
    // constructor
    base() {
        std::cout << "基底クラスのコンストラクタ" << "\n";
    }
    // destructor
    virtual ~base() {
        std::cout << "基底クラスのデストラクタ" << "\n";
    }
};
 
class deriv : public base {
public:
    // constructor
    deriv() {
        std::cout << "派生クラスのコンストラクタ" << "\n";
    }
    // destructor
    ~deriv() {
        std::cout << "派生クラスのデストラクタ" << "\n";
    }
};
 
int main() {
    base *test_ptr = new deriv;
    // 基底クラスのコンストラクタ
    // 派生クラスのコンストラクタ ...と出力される
 
    std::cout << "\n";
 
    delete test_ptr;
    test_ptr = NULL;
    // 派生クラスのデストラクタ ...だけが出力される
 
    return 0;
}

実行結果。

基底クラスのコンストラクタ
派生クラスのコンストラクタ
 
派生クラスのデストラクタ
基底クラスのデストラクタ

と、今度は期待した動作を得られます。

通常、基底クラスの関数ではなく、派生クラスの関数を呼び出したい場合に、virtualキーワードを使用します。
ですので、デストラクタにvirtualキーワードを使う場合とは、意味合いが違います。

デストラクタをvirtual宣言すると、派生クラスのデストラクタが呼び出された後、基底クラスのデストラクタが呼び出される、という動作になります。
virutalキーワードで仮想デストラクタとして宣言することで、基底クラス(base)の型である、派生クラスをインスタンス化した変数を、派生クラスのデストラクタを呼び出して、期待した動作の手順で削除できます。

スポンサーリンク
 
スポンサーリンク