派生クラスのコンストラクタ・デストラクタ
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
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・コンストラクタを明示する
- クラスで自動的に生成されるメンバ関数
- コピーコンストラクタ
- 引数つきのコンストラクタを持つクラス定義
- コンストラクタで自動的に初期化する