参照型の C++ スタック セマンティクス
Visual Studio 2005 より前のバージョンでは、参照型のインスタンスは new
演算子を使用することによってのみ作成できました。それによって、ガベージ コレクション ヒープにオブジェクトが作成されていました。 しかし、現在では、スタックにネイティブ型のインスタンスを作成する場合と同じ構文を使用して、参照型のインスタンスを作成できるようになりました。 そのため、参照型のオブジェクトを作成するために ref new、gcnew を使用する必要はありません。 また、オブジェクトがスコープ外に出た際には、コンパイラによってオブジェクトのデストラクターが呼び出されます。
解説
スタック セマンティクスを使用して参照型のインスタンスを作成すると、コンパイラは内部的に、ガベージ コレクション ヒープ上にインスタンスを作成します (gcnew
が使用されます)。
関数のシグネチャや戻り値の型に、値渡しの参照型のインスタンスが含まれている場合、その関数はメタデータ内で、(modreq を使用した) 特別な処理を必要とするものとしてマークされます。 この特別な処理は、現在、Visual C++ クライアントによってのみ提供されています。他の言語では、現在、スタック セマンティクスで作成された参照型を使用する関数やデータの使用はサポートされていません。
スタック セマンティクスの代わりに gcnew
(動的割り当て) を使用する理由の 1 つは、型にデストラクターがない場合があるということです。 また、関数が Visual C++ 以外の言語によって使用されるようにしたい場合は、スタック セマンティクスを使用して作成された参照型を、関数シグネチャで使用できなくなります。
コンパイラは、参照型に対してコピー コンストラクターを生成しません。 したがって、値渡しの参照型を使用する関数をシグネチャ内で定義する場合は、参照型のコピー コンストラクターを定義する必要があります。 参照型のコピー コンストラクターでは、シグネチャの形式は次のようになります: R(R%){}
。
コンパイラでは、参照型に対する既定の代入演算子は生成されません。 代入演算子を使用すると、スタック セマンティクスを使ってオブジェクトを作成し、それを、スタック セマンティクスを使って作成された既存のオブジェクトで初期化できます。 参照型の代入演算子では、シグネチャの形式は次のようになります: void operator=( R% ){}
。
型のデストラクターによって重要なリソースが解放される場合で、参照型にスタック セマンティクスを使用する場合には、デストラクターを明示的に呼び出す (または delete
を呼び出す) 必要があります。 参照型でのデストラクターについて詳しくは、「デストラクターとファイナライザー (方法: クラスと構造体を定義および使用する (C++/CLI))」をご覧ください。
コンパイラによって生成された代入演算子は、通常の標準 C++ 規則に従いますが、次のことが追加されます。
型が参照型へのハンドルである非静的データ メンバーは、シャロー コピーされます (型がポインターである非静的データ メンバーのように扱われます)。
型が値型である非静的データ メンバーは、シャロー コピーされます。
型が参照型のインスタンスである非静的データ メンバーは、参照型のコピー コンストラクターの呼び出しを呼び出します。
コンパイラには、スタック セマンティクスを使用して作成された参照型のインスタンスを、基になるハンドル型へと変換する、%
単項演算子も用意されています。
次の参照型は、スタック セマンティクスと共には使用できません。
例
説明
次のコード サンプルでは、スタック セマンティクスを使用して参照型のインスタンスを宣言する方法、代入演算子とコピー コンストラクターのしくみ、およびスタック セマンティクスを使用して作成された参照型を使用して追跡参照を初期化する方法が示されています。
コード
// stack_semantics_for_reference_types.cpp
// compile with: /clr
ref class R {
public:
int i;
R(){}
// assignment operator
void operator=(R% r) {
i = r.i;
}
// copy constructor
R(R% r) : i(r.i) {}
};
void Test(R r) {} // requires copy constructor
int main() {
R r1;
r1.i = 98;
R r2(r1); // requires copy constructor
System::Console::WriteLine(r1.i);
System::Console::WriteLine(r2.i);
// use % unary operator to convert instance using stack semantics
// to its underlying handle
R ^ r3 = %r1;
System::Console::WriteLine(r3->i);
Test(r1);
R r4;
R r5;
r5.i = 13;
r4 = r5; // requires a user-defined assignment operator
System::Console::WriteLine(r4.i);
// initialize tracking reference
R % r6 = r4;
System::Console::WriteLine(r6.i);
}
出力
98
98
98
13
13