最終更新:2026-10-12
C++ クラス初期化リストイディオム¶
- constメンバの初期化はコンストラクタの初期化リストでのみ行うことができます
- 参照メンバの初期化もコンストラクタの初期化リストでのみ行うことができます
- オブジェクトメンバ(そのオブジェクトメンバのクラスがデフォルトコンストラクタを持たない場合)の初期化も、コンストラクタの初期化リストでのみ行うことができます
クラス間のネスト¶
第一の方法:初期化リストを使用する。
class Animal {
public:
Animal() {
std::cout << "Animal() is called" << std::endl;
}
Animal(const Animal &) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal &) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog {
public:
Dog(const Animal &animal) : __animal(animal) {
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
private:
Animal __animal;
};
int main() {
Animal animal;
std::cout << std::endl;
Dog d(animal);
std::cout << std::endl;
return 0;
}
実行結果:
Animal() is called
Animal (const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
上から順に分析していきます。
main関数内のAnimal animal;はデフォルトコンストラクタを呼び出します。
Dog d(animal);は次と等価です:
最後にデストラクタが呼び出され、プログラムは終了します。
第二の方法:コンストラクタ内での代入によってオブジェクトを初期化する。
コンストラクタを次のように変更します:
Dog(const Animal &animal) {
__animal = animal;
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
Animal() is called
Animal() is called
Animal & operator=(const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
ここから次のことが分かります:
Dog d(animal);を呼び出すとき、次と等価です:
先にオブジェクトを定義してから代入するため、まずデフォルトコンストラクタが呼び出され、その後に=演算子のオーバーロード関数が呼び出されます。
小まとめ
以上より、次のような結論が得られます:
- クラス内に他の独自定義のclassやstructが含まれる場合、初期化リストを使うと、実際にはオブジェクトの作成と同時に初期化が行われます
- 一方、クラス内で代入する方式は、先にオブジェクトを定義してから代入することと等価であり、一般にまずデフォルトコンストラクタが呼び出され、その後に=演算子のオーバーロード関数が呼び出されます。
デフォルトコンストラクタがない継承関係の場合¶
今度は上記の関係を継承に変更し、AnimalとDogのコンストラクタを次のコードのように修正することを考えます:
class Animal {
public:
Animal(int age) {
std::cout << "Animal(int age) is called" << std::endl;
}
Animal(const Animal & animal) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal & amimal) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog : Animal {
public:
Dog(int age) : Animal(age) {
std::cout << "Dog(int age) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
};
上記は初期化リストを通じて基底クラスの引数付きコンストラクタに引数を渡しています。初期化リストを使わずに渡した場合、どのような影響が生じるでしょうか?
初期化リストを取り除く
プログラムを実行します:Animalにはデフォルトコンストラクタがないためエラーになります。この種の問題は致命的であり、できるだけ避けるべきです。初期化リストを使って基底クラスのコンストラクタを初期化することができます。
クラス内のconstデータメンバ・参照データメンバ¶
特に参照データメンバは、必ず初期化リストで初期化しなければならず、代入による初期化はできません!
例:上記のAnimalに非公開メンバを追加し、コンストラクタを次のように修正します:
class Animal {
public:
Animal(int age,std::string name) {
std::cout << "Animal(int age) is called" << std::endl;
}
private:
int &age_;
const std::string name_;
};
次のようなエラーが出ます:
次のように変更すべきです:
Animal(int age, std::string name) : age_(age), name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
関連トピック¶
- C++ デフォルトコンストラクタの回避 | C++那些事
- C++ 関数テンプレートよりクラステンプレート | C++那些事
- C++ copy-and-swap イディオム | C++那些事
- C++ 依存性注入 | C++那些事
- C++ enum class 強い型付けされた列挙 | C++那些事
- C++ Header-Only と実装分離 | C++那些事
- C++ 遅延評価イディオム | C++那些事
- C++ ルックアップテーブル法イディオム | C++那些事
- C++ メンバポインタと型情報 | C++那些事
- C++ Mixin イディオム | C++那些事