はじめに
C++の継承において、コンストラクタとデストラクタは子クラスに継承されません。これらは、そのクラスのオブジェクトが生成・破棄される際の固有の処理であるため、各クラスが自身のものを持つ必要があります。
しかし、子クラスのオブジェクトが生成される際には、親クラスのメンバも初期化されなければなりません。そのため、C++コンパイラは、子クラスのコンストラクタが実行される前に、親クラスのコンストラクタを自動的に呼び出すという仕組みを提供しています。
この記事では、継承関係にあるクラスのオブジェクトが生成・破棄される際に、親クラスと子クラスのコンストラクタとデストラクタが、どのような順番で呼び出されるのかを、サンプルコードを通して解説します。
コンストラクタ/デストラクタの呼び出し順序を確認するサンプルコード
このコードは、Parent
クラスと、それを継承したChild
クラスを定義します。それぞれのコンストラクタとデストラクタが、呼び出されたことを示すメッセージを出力します。
完成コード
#include <iostream>
using namespace std;
// --- 親クラス(基底クラス) ---
class Parent {
public:
Parent() {
cout << "1. 親クラスのコンストラクタが呼び出されました。" << endl;
}
~Parent() {
cout << "4. 親クラスのデストラクタが呼び出されました。" << endl;
}
};
// --- 子クラス(派生クラス) ---
class Child : public Parent {
public:
Child() {
cout << "2. 子クラスのコンストラクタが呼び出されました。" << endl;
}
~Child() {
cout << "3. 子クラスのデストラクタが呼び出されました。" << endl;
}
};
int main() {
cout << "--- 子クラスのオブジェクトを生成します ---" << endl;
Child myChildObject;
cout << "--- main関数が終了します(オブジェクトが破棄されます) ---" << endl;
return 0;
}
実行結果
--- 子クラスのオブジェクトを生成します ---
1. 親クラスのコンストラクタが呼び出されました。
2. 子クラスのコンストラクタが呼び出されました。
--- main関数が終了します(オブジェクトが破棄されます) ---
3. 子クラスのデストラクタが呼び出されました。
4. 親クラスのデストラクタが呼び出されました。
コードの解説と呼び出し順序のルール
main
関数で Child myChildObject;
という一行が実行されただけで、4つのメッセージが出力されています。これは、コンストラクタとデストラクタが自動的に呼び出されたことを示しています。
コンストラクタの呼び出し順序
親クラス → 子クラス
実行結果の 1.
と 2.
が示すように、子クラス Child
のオブジェクトを生成すると、まず親クラス Parent
のコンストラクタが呼び出され、その後に子クラス Child
のコンストラクタが呼び出されます。
これは理にかなっています。子クラスは親クラスの機能を引き継いでいるため、子クラスの部品が作られる前に、その土台となる親クラスの部品が先に作られて(初期化されて)いなければならないからです。
デストラクタの呼び出し順序
子クラス → 親クラス
実行結果の 3.
と 4.
が示すように、デストラクタはコンストラクタとは逆の順序で呼び出されます。まず子クラス Child
のデストラクタが呼び出され、その後で親クラス Parent
のデストラクタが呼び出されます。
これも、建物を解体するときに、上階(子クラス)から取り壊し、最後に土台(親クラス)を撤去するのと同じで、理にかなった順序です。
まとめ
今回は、C++の継承における、コンストラクタとデストラクタの自動的な呼び出し順序について解説しました。
- オブジェクト生成時(コンストラクタ): 親クラスが先、子クラスが後。
- オブジェクト破棄時(デストラクタ): 子クラスが先、親クラスが後(生成時と逆順)。
コンストラクタとデストラクタは子クラスに継承されませんが、このようにコンパイラが自動的に呼び出しを連鎖させてくれることで、オブジェクトの初期化と後片付けが正しい順序で確実に行われるようになっています。