更新 : 2007 年 11 月
クラス コンストラクタの初期化処理の順序は、Visual C++ 2008 では C++ マネージ拡張から変更されています。
コンストラクタの初期化処理の順序比較
C++ のマネージ拡張では、コンストラクタの初期化処理は、次の順序で行われていました。
基本クラスのコンストラクタが存在する場合、そのコンストラクタが呼び出されます。
クラスの初期化リストが評価されます。
クラス コンストラクタのコード本体が実行されます。
この実行順序は、ネイティブ C++ プログラミングの慣例に従っています。新しい Visual C++ 言語では、CLR クラスについて、次の実行順序が適用されます。
クラスの初期化リストが評価されます。
基本クラスのコンストラクタが存在する場合、そのコンストラクタが呼び出されます。
クラス コンストラクタのコード本体が実行されます。
この変更が該当するのは CLR のクラスだけです。Visual C++ 2008 のネイティブ クラスについては、従来と同じ規則が適用されます。どちらの場合も、これらの規則は、特定のクラスの階層全体を上にたどって適用されます。
次のコードは、C++ のマネージ拡張を使用した例です。
__gc class A
{
public:
A() : _n(1)
{
}
protected:
int _n;
};
__gc class B : public A
{
public:
B() : _m(_n)
{
}
private:
int _m;
};
前述したコンストラクタの初期化処理順序に従うと、クラス B のインスタンスを新たに作成すると、次の順で初期化が実行されます。
基本クラス A のコンストラクタが呼び出されます。_n メンバが 1 に初期化されます。
B クラスの初期化リストが評価されます。_m メンバが 1 に初期化されます。
B クラスのコード本体が実行されます。
同じコードを Visual C++ の新しい構文で記述すると次のようになります。
ref class A
{
public:
A() : _n(1)
{
}
protected:
int _n;
};
ref class B : A
{
public:
B() : _m(_n)
{
}
private:
int _m;
};
新しい構文で B クラスのインスタンスを新たに作成した場合の実行順序は次のとおりです。
B クラスの初期化リストが評価されます。_m メンバが 0 に初期化されます (_m クラス メンバの未初期化の値は 0)。
基本クラス A のコンストラクタが呼び出されます。_n メンバが 1 に初期化されます。
B クラスのコード本体が実行されます。
以上のコード例から、同様の構文でも、結果が異なることがわかります。B クラスのコンストラクタは、基本クラスの値に依存しています。つまり、基本クラス A の値を使ってそのメンバを初期化します。しかし、その段階では、A クラスのコンストラクタは呼び出されていません。この依存関係が特に問題となるのは、メモリまたはリソースの割り当てが基本クラスのコンストラクタで実行されることを前提に継承クラスが設計されている場合です。
C++ マネージ拡張から Visual C++ 2005 に移行する際の影響
継承クラスの動作が基本クラスに影響することはないので、ほとんどの場合、クラス コンストラクタの実行順序をプログラマが意識する必要はありません。ただし、これらのコード例が示すように、継承クラスの初期化リストが基本クラスのメンバの値に依存している場合、継承クラスのコンストラクタが大きな影響を受けます。C++ のマネージ拡張のコードを新しい構文に移行する場合は、このような初期化コードを、(最後に実行されることが保証される) クラス コンストラクタの本体に移動できないか検討してください。