騒音のない世界 BLOG

コンピュータと、音楽と。

C++の初期化をざっくり抑える

まえがき

久々のプログラミング記事です。

「C++ 初期化」でググるとトップに出てきたこちらの記事、詳細に書かれておりたいへん参考になるのですが、2010年の記事ということもあり参照している国際規格が古くなってしまい、初期化周りの挙動が変わってしまっておりました。

C++ の初期化 - プログラミングの教科書を置いておくところ

例えば、現在プロジェクトで使っているC++11と対応する標準規格はN3337のようなのですが、「デフォルト初期化」の項を見ると、 otherwise, the object is zero-initialized.となっていた部分がotherwise, no initialization is performed.に変わっています。

初期化は基本的な挙動ですが、人に聞かれたときにすぐに説明できなかったので改めて調べてみることにしました。この記事ではcppreference.comを参照していこうとおもいます。

初期化の種類

C++ language - cppreference.com を参照すると、Initializationの項には以下の初期化があります。

  • Default initialization
  • Value initialization (C++03)
  • Copy initialization
  • Direct initialization
  • Aggregate initialization
  • List initialization (C++11)
  • Reference initialization
  • Static non-local initialization
    • zero - constant
  • Dynamic non-local initialization
    • ordered - unordered
  • Copy elision

とりあえず把握しておくべきなのは「デフォルト初期化(Default initialization)」と「値初期化(Value initialization)」あたりかなと思うので今回はそこを中心に紹介しようと思います。ざっくり言えば、カッコを付けずに初期化するかカッコを付けて初期化するかの違いです。挙動の違いを見ていきます。

※ 以下「ゼロ初期化」という言葉が出てきますが、直感的にはゼロやnullptrなどで初期化される、ぐらいの理解でいいと思います。詳しくは Zero initialization - cppreference.com

デフォルト初期化

T object
new T

こういうのがデフォルト初期化になります。

クラスの場合は基本的にはデフォルトコンストラクタ(引数なしコンストラクタ)が呼ばれます。ユーザー定義のデフォルトコンストラクタがあればそちらが呼ばれるし、無ければ自動生成されたコンストラクタ(空のコンストラクタ)が呼ばれます。

例えば、std::stringなどはデフォルトコンストラクタが呼ばれて空文字になります。

しかし、intなどの組み込み型の場合はゼロ初期化されることはなく、不定の値になるので注意が必要です。不定の値を使うのは大抵のケースで未定義の動作となってしまい、不具合の原因になります。

値初期化

T()
new T()
T object{}

こういうのが値初期化になります。C++11以降は波カッコでも使えます。

こちらはユーザー定義のデフォルトコンストラクタがあるかどうかで挙動が変わるようです。ユーザー定義のデフォルトコンストラクタがあれば、単にそれが呼ばれます。

ユーザー定義のデフォルトコンストラクタが無い場合がデフォルト初期化と違っており、ゼロ初期化された後に自動生成のデフォルトコンストラクタが呼ばれます。

(C++11から若干仕様が変わっているようですが、結果はほぼ同じと思います。)

class/structメンバの初期化

クラスメンバの初期化は、初期化を明示的に書かなければデフォルト初期化、それ以外は書いたとおりに初期化される、というイメージで良いと思います。カッコをつければ値初期化になりますし、代入式にするとコピー初期化になるようです。

Non-static data members - cppreference.comMember initializationの節に説明があります。

気をつけるポイントまとめ

  • int hoge;のように組み込み型の変数を定義すると不定な値で初期化されます。
    • 不定な値を利用すると大抵の場合、未定義の動作になります。
  • クラスメンバでintなどの組み込み型を明示的に初期化していない場合、使うときに不定になる可能性があります。
    • ユーザー定義のデフォルトコンストラクタがなく、使うときに値初期化した場合のみゼロ初期化してくれます。

おわりに

今回はデフォルト初期化と値初期化を紹介しましたが、他にも色々な初期化があります。cppreferenceを全部読むのは大変ですが、重要そうなところをざっと読んでみると発見が色々あって面白いかもしれません。

ちなみに、デフォルト初期化のページで面白かったところ。

int x;               // OK: the value of x is indeterminate
int y = x;           // undefined behavior
unsigned char c;     // OK: the value of c is indeterminate
unsigned char d = c; // OK: the value of d is indeterminate

unsigned charの場合のみ、不定の値を代入するのが未定義の動作ではないらしいです。なんで。

ではまた。