基底クラスおよびメンバーの初期化 (C++ のみ)
コンストラクターは、次に示す 2 とおりの異なった方法でメンバーを初期化できます。コンストラクターは渡された引数を使用して、コンストラクター定義内のメンバー変数を
初期化することができます。
complx(double r, double i = 0.0) { re = r; im = i; }
またはコンストラクターは、定義の中に初期化指定子リスト を
含めることができますが、それらは、コンストラクター本体の前に置く必要があります。
complx(double r, double i = 0) : re(r), im(i) { /* ... */ }
どちらの方法でも、引数値は適切なクラスのデータ・メンバーに 割り当てられます。
コンストラクター宣言の一部ではなく、
コンストラクター定義の一部として初期化リストをインクルードします。次に例を示します。
#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 も初期化済みです。
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)を参照してください。

