はじめに
C++の継承において、子クラスのオブジェクトが生成される際には、必ず親クラスのコンストラクタが先に呼び出されます。もし親クラスに引数なしのデフォルトコンストラクタしか存在しない場合は、コンパイラが自動でそれを呼び出してくれます。
しかし、親クラスが引数付きのコンストラクタを持つ場合、子クラス側で「親クラスのどのコンストラクタを、どの引数で呼び出すか」を明示的に指定する必要があります。
この指定を行うのが、「メンバ初期化子リスト」という特別な構文です。この記事では、RPGのキャラクターを例に、子クラス(ナイト)のコンストラクタから、親クラス(キャラクター)の引数付きコンストラクタを呼び出すための、基本的で重要な方法を解説します。
親クラスのコンストラクタを呼び出すサンプルコード
このコードは、Character
(親クラス)と、それを継承した Knight
(子クラス)を定義します。Knight
のコンストラクタが、Character
のコンストラクタを明示的に呼び出す方法を示します。
完成コード
#include <iostream>
#include <string>
using namespace std;
// --- 親クラス(基底クラス) ---
class Character {
private:
string name;
int hp;
public:
Character(); // デフォルトコンストラクタ
Character(string n, int h); // 引数付きコンストラクタ
void showStatus();
};
// --- 子クラス(派生クラス) ---
class Knight : public Character {
private:
int swordLevel;
public:
Knight(); // デフォルトコンストラクタ
Knight(string n, int h, int sl); // 引数付きコンストラクタ
void showKnightStatus();
};
// --- 親クラスの実装 ---
Character::Character() {
name = "名無し";
hp = 100;
cout << "Characterのデフォルトコンストラクタが呼ばれました。" << endl;
}
Character::Character(string n, int h) {
name = n;
hp = h;
cout << "Characterの引数付きコンストラクタが呼ばれました。" << endl;
}
void Character::showStatus() {
cout << "名前: " << name << endl;
cout << "HP: " << hp << endl;
}
// --- 子クラスの実装 ---
Knight::Knight() {
swordLevel = 1;
cout << "Knightのデフォルトコンストラクタが呼ばれました。" << endl;
}
// ★★★ ここがポイント ★★★
Knight::Knight(string n, int h, int sl) : Character(n, h) {
swordLevel = sl;
cout << "Knightの引数付きコンストラクタが呼ばれました。" << endl;
}
void Knight::showKnightStatus() {
showStatus(); // 親クラスのメンバ関数を呼び出し
cout << "剣レベル: " << swordLevel << endl;
}
int main() {
// 子クラスの引数付きコンストラクタを呼び出す
Knight myKnight("アルス", 150, 5);
cout << "\n--- ナイトのステータス ---" << endl;
myKnight.showKnightStatus();
return 0;
}
実行結果
Characterの引数付きコンストラクタが呼ばれました。
Knightの引数付きコンストラクタが呼ばれました。
--- ナイトのステータス ---
名前: アルス
HP: 150
剣レベル: 5
コードの解説
Knight::Knight(string n, int h, int sl) : Character(n, h)
これが、親クラスの引数付きコンストラクタを呼び出すための「メンバ初期化子リスト」です。
:
(コロン): 子クラスのコンストラクタの引数リスト()
の直後にコロンを記述し、初期化子リストを開始します。Character(n, h)
: 親クラス名(引数)
の形式で、呼び出したい親クラスのコンストラクタを指定します。子クラスのコンストラクタが受け取った引数n
とh
を、そのまま親クラスのCharacter(string n, int h)
コンストラクタに渡しています。
呼び出しの流れ
Knight myKnight("アルス", 150, 5);
が実行されます。- コンパイラは、引数に一致する子クラスのコンストラクタ
Knight(string n, int h, int sl)
を見つけます。 - そのコンストラクタのメンバ初期化子リスト
: Character(n, h)
を見て、まず先に親クラスの引数付きコンストラクタCharacter("アルス", 150)
を呼び出します。 - 親クラスのコンストラクタの処理(
name
とhp
の設定)が完了します。 - 次に、子クラスのコンストラクタ
{}
の本体の処理(swordLevel = sl;
)が実行されます。 - 子クラスのコンストラクタの処理が完了します。
もし初期化子リストを省略したら?
もし Knight
の引数付きコンストラクタで : Character(n, h)
の部分を省略した場合、コンパイラは自動的に親クラスの引数なしのデフォルトコンストラクタ (Character()
) を呼び出そうとします。
まとめ
今回は、C++の継承において、子クラスから親クラスの引数付きコンストラクタを呼び出す方法を解説しました。
- 子クラスのコンストラクタから親クラスのコンストラクタを明示的に呼び出すには、「メンバ初期化子リスト」を使う。
- 構文は、
子コンストラクタ(...) : 親クラス名(引数) { ... }
となる。 - 初期化子リストを省略すると、親クラスのデフォルトコンストラクタが自動的に呼び出される。
この仕組みを理解することで、親クラスで定義された初期化処理を子クラスで再利用し、効率的で正しくオブジェクトを構築することができます。