C++の初期化は分かりにくい
概要
C++の初期化関係が複雑に感じたのでまとめました。
初期化の種類
まず、以下の3つの初期化が存在するのが分かりにくいです。
// デフォルト初期化 Foo foo; // 値初期化 Foo foo{}; auto foo = Foo(); // ゼロ初期化 // 常に0クリア。不定値を持たない。 static Foo foo;
デフォルト初期化
特徴としては多くの場合初期化されません。
struct Case1{ int a; }; struct Case2{ Case2(){} int a; }; struct Case3{ int a = 0; }; struct Case4{ int a; Case1 b; }; struct Case5{ int a; Case2 b; }; struct Case6{ int a; Case3 b; }; struct Case7{ Case7(){} int a; Case1 b; }; struct Case8{ Case8(){} int a; Case2 b; }; struct Case9{ Case9(){} int a; Case3 b; };
上記のクラスの中で正しく初期化される(不定値を持たない)のはCase3のみです。
値初期化
newで()を付けた場合やコンストラクタでの:を使った初期化等はこちらです。
特徴としては多くの場合0クリアされます。
Case8とCase9のみ正しく初期化されません。
Case7が正しく0クリアされるのはビックリする挙動ですよね。
規格で該当する箇所を見つける事ができなかったのでclang特有の物だった場合すみません。
初期化動作の詳細
この辺りが詳しいです。
C++ の初期化 - プログラミングの教科書を置いておくところ
C++11: Syntax and Feature
8.5 初期化子(Initializers)
memsetの問題点
以下の様なコードで対処する事が多いと思います。
Foo::Foo(){ std::memset(this, 0, sizeof(Foo)); }
これは以下の問題点があります。
- メンバが全てPODじゃないとメンバデータを壊してしまう。
- Fooクラスがstandard-layout classじゃないと不適切な領域をクリアしてしまう。
- 初期化を2回している。
結論
データしか無いクラスの初期化にはCase3と同じ形式を使いましょう。
struct Foo{ int a = 0; unsigned int b = 0; char c = 0; };