基底クラスおよびメンバーの初期化 (C++ のみ)

コンストラクターは、次に示す 2 とおりの異なった方法でメンバーを初期化できます。コンストラクターは渡された引数を使用して、コンストラクター定義内のメンバー変数を 初期化することができます。
complx(double r, double i = 0.0) { re = r; im = i; }
またはコンストラクターは、定義の中に初期化指定子リスト を 含めることができますが、それらは、コンストラクター本体の前に置く必要があります。
complx(double r, double i = 0) : re(r), im(i) { /* ... */ }

どちらの方法でも、引数値は適切なクラスのデータ・メンバーに 割り当てられます。

構文図を読む構文図をスキップする
 初期化指定子リストの構文 

      .-,---------------------------------------------------.   
      |                    .---------------------------.    |   
      V                    V                           |    |   
>>-:----+-identifier-+--(----+-----------------------+-+--)-+--><
        '-class_name-'       '-assignment_expression-'          

コンストラクター宣言の一部ではなく、 コンストラクター定義の一部として初期化リストをインクルードします。次に例を示します。
#include <iostream>
using namespace std;

class B1 {
  int b;
public:
  B1() { cout << "B1::B1()" << endl; };

  // inline constructor
  B1(int i) : b(i) { cout << "B1::B1(int)" << endl; }
};
class B2 {
  int b;
protected:
  B2() { cout << "B2::B2()" << endl; }

  // noninline constructor
  B2(int i);
};

// B2 constructor definition including initialization list
B2::B2(int i) : b(i) { cout << "B2::B2(int)" << endl; }

class D : public B1, public B2 {
  int d1, d2;
public:
  D(int i, int j) : B1(i+1), B2(), d1(i) {
    cout << "D1::D1(int, int)" << endl;
    d2 = j;}
};

int main() {
  D obj(1, 2);
}
この例の出力は次のようになります。
B1::B1(int)
B2::B2()
D1::D1(int, int)
コンストラクターのある基底クラスまたはメンバーをコンストラクターを呼び出すことによって、 明示的に初期化するのでない場合、コンパイラーは、 自動的にデフォルト・コンストラクターのある基底クラスまたはメンバーを初期化します。 上記の例では、クラス D のコンストラクター内の呼び出し B2() を除外すると (後に示すように)、空の式リストのあるコンストラクター初期化指定子が自動的に作成されて、B2 を初期化します。 クラス D のコンストラクター (上記と下記に示されて います) は、クラス D のオブジェクトと同じ構造体になります。
class D : public B1, public B2 {
  int d1, d2;
public:

  // call B2() generated by compiler
  D(int i, int j) : B1(i+1), d1(i) {
    cout << "D1::D1(int, int)" << endl;
    d2 = j;}
};

上記の例では、コンパイラーは、B2() のデフォルトのコンストラクターを自動的に呼び出します。

派生クラスがコンストラクターを呼び出すことができるようにするには、 コンストラクターを public または protected 付きで宣言する必要があります。 次に例を示します。
class B {
  B() { }
};

class D : public B {

  // error: implicit call to private B() not allowed
  D() { }
};

コンパイラーは、コンストラクターが private コンストラクター B::B() にアクセスできないので、D::D() の定義を許可しません。

初期化指定子リストを指定して、デフォルト・コンストラクターのない基底クラス、参照データ・メンバー、 非静的 const データ・メンバー、または定数データ・メンバーを含むクラス型、といったケースを初期化する必要があります。 次の例は、このことを示しています。
class A {
public:
  A(int) { }
};

class B : public A {
  static const int i;
  const int j;
  int &k;
public:
  B(int& arg) : A(0), j(1), k(arg) { }
};

int main() {
  int x = 0;
  B obj(x);
};

データ・メンバー j および k、 さらに基底クラス A は、B のコンストラクターの初期化指定子リストで初期化される必要があります。

クラスのメンバーを初期化する際、データ・メンバーを使用できます。 次の例は、このことを示しています。
struct A {
  int k;
  A(int i) : k(i) { }
};
struct B: A {
  int x;
  int i;
  int j;
  int& r;
  B(int i): r(x), A(i), j(this->i), i(i) { }
};

コンストラクター B(int i) は、次の項目を初期化します。

  • B::x を参照するための B::r
  • B(int i) への引数の値を指定したクラス A
  • B::i の値を指定した B::j
  • B(int i) への引数の値を指定した B::i
クラスのメンバーを初期化する場合、メンバー関数 (仮想メンバー関数を含む) を呼び出したり、あるいは演算子 typeid または dynamic_cast を使用することもできます。 しかし、すべての基底クラスが初期化される前に、 メンバー初期化リストにあるこれらの演算を実行する場合、その動作は、未定義です。 次の例は、このことを示しています。
#include <iostream>
using namespace std;

struct A {
  int i;
  A(int arg) : i(arg) {
    cout << "Value of i: " << i << endl;
  }
};

struct B : A {
  int j;
  int f() { return i; }
  B();
};

B::B() : A(f()), j(1234) {
   cout << "Value of j: " << j << endl;
}

int main() {
  B obj;
}
上記の例の出力は、次の結果のようになります。
Value of i: 8
Value of j: 1234
B のコンストラクターの初期化指定子 A(f()) の動作は、未定義です。 ランタイムは、B::f() を呼び出し、基底 A が初期化されていなくても、A::i にアクセスしようとします。
次の例は、B::B() の初期化指定子が異なる引数を持つ点を除いては、直前の例と同じです。
#include <iostream>
using namespace std;

struct A {
  int i;
  A(int arg) : i(arg) {
    cout << "Value of i: " << i << endl;
  }
};

struct B : A {
  int j;
  int f() { return i; }
  B();
};

B::B() : A(5678), j(f()) {
   cout << "Value of j: " << j << endl;
}

int main() {
  B obj;
}
上記の例の出力は、次のとおりです。
Value of i: 5678
Value of j: 5678
B のコンストラクターでは、初期化指定子 j(f()) の動作は、明確に定義されています。 B::j が初期化される時、基底クラス A も初期化済みです。
C++11
委任コンストラクター機能が使用可能な場合、初期化は非委任コンストラクター内でのみ実行できます。すなわち、委任コンストラクターは委任および初期化の両方は実行できません。次の例を検討してみます。
struct A{
  int x,y;
  A(int x):x(x),y(0){}

  /* the following statement is not allowed */
  A():y(0),A(42) {}
}
コンストラクター A() は、A(int x) に委任しますが、A() は初期化も実行します。これは許可されません。コンパイラーはエラーを出して、違反を示します。

詳細については、委任コンストラクター (C++11)を参照してください。

C++11