派生クラスのコンストラクタ・デストラクタ
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
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)の型である、派生クラスをインスタンス化した変数を、派生クラスのデストラクタを呼び出して、期待した動作の手順で削除できます。
- - 関連記事 -
- 派生クラスによる関数の隠蔽
- 仮想クラス
- 抽象クラス
- 仮想関数・virtualキーワード
- 親クラスの引数ありコンストラクタを子クラスから初期化・動的サイズのスタック
- クラスのアクセス制限キーワード・private, protected, public
- スタックを実装したクラスを拡張する
- クラス継承・親クラスと子クラス
- static・静的メンバ関数
- static・静的メンバ変数
- 定数のconstメンバ関数をクラスに宣言
- 定数のconstメンバ変数をクラスに宣言
- フレンドクラス・friend
- フレンド関数・friend
- クラスの中にメンバ関数(メソッド)を実装する
- explicit・コンストラクタを明示する
- クラスで自動的に生成されるメンバ関数
- コピーコンストラクタ
- 引数つきのコンストラクタを持つクラス定義
- コンストラクタで自動的に初期化する