クラスの初期化
コンストラクタ:インデックス への移動
パブリックメンバーのみを持ち,コンストラクタまたは基本クラス(通常は構造体)を持たないクラスのオブジェクトは,初期化子リストで初期化できます。クラスがコンストラクタを持つ場合,そのオブジェクトは初期化されるか,デフォルトのコンストラクタを持たなければなりません。後者は,明示的(explicit)に初期化されないオブジェクトに適用されます。
コンストラクタを持つクラスのオブジェクトは,カッコ内の式リストで初期化できます。このリストは,そのコンストラクタへの引数リストとして使われます。また,等号の後に単独の値を指定する方法を用いることもできます。単独の値は,そのクラスのコンストラクタが受け入れる最初の引数と同一の型になり,この場合は引数はそれ以上ないか,あるいは残りの引数はデフォルトの値を持つことになります。この単独の値は,そのクラス型のオブジェクトであってもかまいません。前者のケースでは,一致するコンストラクタがオブジェクトを作成するために呼び出されます。後者のケースでは,コピーコンストラクタがオブジェクトの初期化を行うために呼ばれます。
class X
{
int i;
public:
X(); // 関数本体は省略
X(int x);
X(const X&);
};
void main()
{
X one; // デフォルトのコンストラクタが呼び出される
X two(1); // コンストラクタ X::X(int) が使われる
X three = 1; // X::X(int) を呼び出す
X four = one; // X::X(const X&) を呼び出してコピーを行う
X five(two); // X::X(const X&) を呼び出す
}
コンストラクタは,2 通りの方法でそのメンバーに値を代入できます。
1. 値をパラメータとして受け入れ,そのコンストラクタの関数本体内でメンバー変数に代入する
class X
{
int a, b;
public:
X(int i, int j) { a = i; b = j }
};
2. 関数本体の前に初期化子リストを置くことができる
class X
{
int a, b, &c; // 参照変数に注意
public:
X(int i, int j) : a(i), b(j), c(a) {}
};
初期化子リストでのみ参照変数の初期化が行われます。
どちらの場合でも,X x(1, 2) の初期化は,値 1 を x::a に,値 2 を x::b に代入します。2 番目の初期化子リストを用いる方法は,基本クラスコンストラクタに値を渡す機構を提供します。
メモ: 基本クラスのコンストラクタは,派生クラスから呼び出せるように,public または protected として宣言しなければなりません。
class base1
{
int x;
public:
base1(int i) { x = i; }
};
class base2
{
int x;
public:
base2(int i) : x(i) {}
};
class top : public base1, public base2
{
int a, b;
public:
top(int i, int j) : base1(i*5), base2(j+i), a(i) { b = j;}
};
このクラス階層では,top one(1, 2) の宣言によって,base1 は値 5 で初期化され,base2 は値 3 で初期化されます。初期化の方法は混在してもかまいません。
「コンストラクタの呼び出し順序」で前述したように,基本クラスは宣言順に初期化されます。その後でメンバーがやはり(初期化リストとは関係なく)宣言順に初期化されます。
class X
{
int a, b;
public:
X(int i, j) : a(i), b(a+j) {}
};
このクラスでは,X x(1, 1) の宣言によって,x::a に 1 が代入され,x::b に 2 が代入されます。
基本クラスコンストラクタは,すべての派生クラスメンバーの構築に先だって呼び出されます。派生クラスの値を変更しても,基本クラスの作成に影響はありません。
class base
{
int x;
public:
base(int i) : x(i) {}
};
class derived : base
{
int a;
public:
derived(int i) : a(i*10), base(a) { } // 注意! base には初期化
// されていない a が渡される
};
このクラスの設定では,派生クラス d(1) の呼び出しによって,基本クラスメンバー x の値は 10 にはなりません。基本クラスコンストラクタに渡される値は未定義になります。
インラインでないコンストラクタ中に初期化子リストが必要なとき,そのリストをクラス定義内に置かないでください。かわりに,関数が定義される位置に入れてください。
derived::derived(int i) : a(i)
{
.
.
.
}