コンストラクターは、次に示す 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) B1::B1() 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) は、次のことを初期化します。
クラスのメンバーを初期化する場合、メンバー関数 (仮想メンバー関数を含む) を呼び出したり、あるいは演算子 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: 1234B のコンストラクターの初期化指定子 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: 5678B のコンストラクターでは、初期化指定子 j(f()) の振る舞いは、明確に定義されています。 B::j が初期化される時、基底クラス A も初期化済みです。
関連参照