C++ メンバ初期化子リスト|コンストラクタ初期化子/メンバ初期化子

メンバ初期化子リスト

コンストラクタの定義時にメンバ変数を初期化するために用いられる記述はメンバ初期化子リスト(member initializer list)と呼ばれている。メンバ初期化子リストによる初期化は、コンストラクタ本体の処理が実行される前の段階で行われる。つまり、メンバ初期化子リストの利用によって、クラスのオブジェクトが構築されるタイミングでメンバ変数を直接的に初期化することができるようになる。

struct T {
  int member;
  T() :
    member(9) // メンバ初期化子リストによる直接初期化
  {
    // 以下の場合は代入処理という扱いとなり厳密な初期化ではない
    // データメンバが初期化された後の処理であり余計なコストとなる場合もある
    // member = 8;
  }
};

T().member; // 9

目次

コンストラクタ初期化子/メンバ初期化子

コンストラクタ初期化子は、コンストラクタの定義内で基底クラスやメンバ変数を初期化するために用いられる記述である。初期化する各要素はメンバ初期化子と呼ばれる。コンストラクタ初期化子は:記号とそれに続くメンバ初期化子の並び(メンバ初期化子リスト)によって表される(: メンバ初期化子, メンバ初期化子, ...)。各メンバ初期化子には直接初期化のために丸括弧による式リストを指定することができるほか、C++11以降では波括弧による初期化リストの利用が可能となっている。空の括弧が用いられた場合は値初期化される(基本型の場合はゼロ初期化に相当)。

struct T {
  int a, b, c;
  T() : a(1), b{2}, c() {} // `a(1)`と`b{2}`と`c()`はメンバ初期化子
  // `: a(1), b{2}, c()`はコンストラクタ初期化子
  //   `a(1), b{2}, c()`はメンバ初期化子リスト
};

T().a; // 1
T().b; // 2
T().c; // 0

コンストラクタ初期化子では、基底クラスのコンストラクタを明示的に呼び出すことも可能となっている。

struct A { A() {} };
struct B : A {
  A a;
  B()
    : A() // 基底クラスAのコンストラクタが呼ばれる
    , a() // メンバ変数のコンストラクタが呼ばれる
  {}
};

メンバ初期化子の初期化順序

コンストラクタ初期化子では、まず基底クラスのコンストラクタが優先的に初期化される。その後は各メンバーがクラス定義時におけるメンバ変数の宣言順に初期化される。そして最後に自身のコンストラクタ本体に処理が移る。

struct A { A() {} };
struct B { B() {} };
struct C : B, A {
  int a;
  int b;
  C()
    : b(a + 1) // これらの順序は無視される
    , a(1)
    , A()
    , B()
  {} // `B()`, `A()`, `a(1)`, `b(a + 1)`の順番で呼び出される
};

C().b == 2; // true

メンバ初期化子を省略した場合の挙動

ユーザ定義のコンストラクタでメンバ初期化子の記述を省略した場合には、基底クラスのデフォルトコンストラクタや、メンバ変数のデフォルトコンストラクタが自動的に呼び出される。基本型のメンバ変数の場合は不定な値となる。

struct A {
  int i;
  A() {
    i != 0; // ゼロとは限らない
  }
};

struct B : A {
  A a;
  B() {
    // 既にメンバ親クラスAのデフォルトコンストラクタは呼ばれている
    // 既にメンバ変数`A a`のデフォルコンストラクタは呼ばれている
    a = A(); // 初期化ではなく代入処理になってしまう
  }
};

ゼロ初期化が必要な場合は空の丸括弧や波括弧を用いたメンバ初期化子を指定する必要がある(A() : i() {})。

コンストラクタ内でのメンバ変数に対する代入処理(a = A();)は、初期化処理ではなくコピー代入処理となるため注意したい。A()による一時オブジェクトを生成するためのコストと、代入処理時のコピーコストが余計に発生してしまう恐れがある。

基底クラスのデフォルトコンストラクタが未定義または非公開だった場合にはエラーが発生する。

参考:デフォルトコンストラクタの暗黙呼び出し

メンバ宣言時に初期化子が指定された場合の挙動

メンバ初期化子が省略された場合には、メンバ宣言時の初期化式が優先される。以下の例の場合、コンストラクタ本体に処理が移る前の段階でint i = 9;によるメンバの初期化が行われることになる。

struct T {  int i = 9;  T() {}  };
T().i == 9; // true

メンバ初期化子が指定された場合には、メンバ初期化子による初期化が優先され、デフォルトメンバ初期化子は機能しなくなる。以下の例の場合、int i = 9;による初期化処理自体が行われなくなる。

struct T {  int i = 9;  T() : i(8) {}  };
T().i == 8; // true
広告