ぷろみん

プログラミング的な内容を扱ってます

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;
};

続き newしてもゼロクリアされる訳じゃない