オブジェクト演算子 (^) へのハンドル (C++/CLI および C++/CX)
ハンドル宣言子 (^
、"ハット" と読みます) は、指定子の型を変更します。この指定子の型は、宣言されたオブジェクトがアクセス不能になったとシステムが判断すると、そのオブジェクトが自動的に削除されることを意味します。
宣言されたオブジェクトへのアクセス
ハンドル宣言子を指定して宣言した変数は、オブジェクトへのポインターのように動作します。 ただし、変数はオブジェクト全体を指し示します。オブジェクトのメンバーを指すことはできません。また、ポインター演算をサポートしません。 オブジェクトにアクセスするには間接演算子 (*
) を使用し、オブジェクトのメンバーにアクセスするには矢印のメンバー アクセス演算子 (->
) を使用します。
Windows ランタイム
コンパイラは、COM 参照カウント メカニズムを使用して、オブジェクトがもう使用されず、削除してよいかどうかを判断します。 これが可能なのは、Windows ランタイム インターフェイスから派生するオブジェクトが実際に COM オブジェクトであるためです。 参照カウントは、オブジェクトが作成されるかコピーされるとインクリメントされ、オブジェクトが null に設定されるかスコープから外れるとデクリメントされます。 参照カウントがゼロになると、オブジェクトは直ちに自動的に削除されます。
ハンドル宣言子の利点は、煩雑でエラーが発生しやすいプロセスであるオブジェクトについて、COM では参照カウントを明示的に管理する必要があるということです。 つまり、参照カウントをインクリメントおよびデクリメントするには、オブジェクトの AddRef() メソッドと Release() メソッドを呼び出す必要があります。 ただし、ハンドル宣言子を使用してオブジェクトを宣言する場合、コンパイラでは、参照カウントを自動的に調整するコードを生成します。
オブジェクトをインスタンス化する方法の詳細については、「ref new」を参照してください。
要件
コンパイラ オプション: /ZW
共通言語ランタイム
システムは、CLR ガベージ コレクター メカニズムを使用して、オブジェクトがもう使用されず、削除してよいかどうかを判断します。 共通言語ランタイムが、オブジェクトを割り当てるヒープを保持し、プログラムでマネージド参照 (変数) を使用します。それによりヒープ内のオブジェクトの場所が示されます。 オブジェクトがもう使用されなくなると、オブジェクトがヒープで占有していたメモリが解放されます。 ガベージ コレクターは、解放済みメモリの使用を改善するために、定期的にヒープを圧縮します。 ヒープを圧縮すると、ヒープのオブジェクトを移動できます。これにより、マネージド参照によって参照される場所が無効になります。 ただし、ガベージ コレクターは、すべてのマネージド参照の場所を認識しており、ヒープ内のオブジェクトの現在の場所を示すようにマネージド参照を自動的に更新します。
ネイティブ C++ ポインター (*
) と参照 (&
) は、マネージド参照ではないため、それらが指し示すアドレスをガベージ コレクターは自動的に更新できません。 この問題を解決するには、ガベージ コレクターが認識しており自動的に更新できる変数を、ハンドル宣言子を使用して指定します。
詳細は、方法: ネイティブ型でハンドルを宣言するを参照してください。
例
このサンプルは、マネージド ヒープ上で参照型のインスタンスを作成する方法を示しています。 また、このサンプルでは、1 つのハンドルを別のハンドルで初期化することができ、その結果、ガベージ コレクトされたマネージド ヒープ上で同じオブジェクトへの参照が 2 つ作成されることも示しています。 1 つのハンドルに nullptr を割り当てても、オブジェクトはガベージ コレクションの対象としてマークされないことに注意してください。
// mcppv2_handle.cpp
// compile with: /clr
ref class MyClass {
public:
MyClass() : i(){}
int i;
void Test() {
i++;
System::Console::WriteLine(i);
}
};
int main() {
MyClass ^ p_MyClass = gcnew MyClass;
p_MyClass->Test();
MyClass ^ p_MyClass2;
p_MyClass2 = p_MyClass;
p_MyClass = nullptr;
p_MyClass2->Test();
}
1
2
次の例では、マネージド ヒープ上のオブジェクト (オブジェクトの型はボックス化された値型) へのハンドルを宣言する方法を示しています。 また、このサンプルでは、ボックス化されたオブジェクトから値型を取得する方法も示します。
// mcppv2_handle_2.cpp
// compile with: /clr
using namespace System;
void Test(Object^ o) {
Int32^ i = dynamic_cast<Int32^>(o);
if(i)
Console::WriteLine(i);
else
Console::WriteLine("Not a boxed int");
}
int main() {
String^ str = "test";
Test(str);
int n = 100;
Test(n);
}
Not a boxed int
100
この例では、任意のオブジェクトを指し示すvoid*
ポインターを使用した C++ の共通の表現形式が、任意の参照クラスへのハンドルも保持できる Object^
で置き換えられることを示しています。 また、配列やデリゲートなど、すべての型がオブジェクト ハンドルに変換できることも示しています。
// mcppv2_handle_3.cpp
// compile with: /clr
using namespace System;
using namespace System::Collections;
public delegate void MyDel();
ref class MyClass {
public:
void Test() {}
};
void Test(Object ^ x) {
Console::WriteLine("Type is {0}", x->GetType());
}
int main() {
// handle to Object can hold any ref type
Object ^ h_MyClass = gcnew MyClass;
ArrayList ^ arr = gcnew ArrayList();
arr->Add(gcnew MyClass);
h_MyClass = dynamic_cast<MyClass ^>(arr[0]);
Test(arr);
Int32 ^ bi = 1;
Test(bi);
MyClass ^ h_MyClass2 = gcnew MyClass;
MyDel^ DelInst = gcnew MyDel(h_MyClass2, &MyClass::Test);
Test(DelInst);
}
Type is System.Collections.ArrayList
Type is System.Int32
Type is MyDel
このサンプルは、ハンドルが逆参照できること、および逆参照されるハンドル経由でメンバーにアクセスできることを示しています。
// mcppv2_handle_4.cpp
// compile with: /clr
using namespace System;
value struct DataCollection {
private:
int Size;
array<String^>^ x;
public:
DataCollection(int i) : Size(i) {
x = gcnew array<String^>(Size);
for (int i = 0 ; i < Size ; i++)
x[i] = i.ToString();
}
void f(int Item) {
if (Item >= Size)
{
System::Console::WriteLine("Cannot access array element {0}, size is {1}", Item, Size);
return;
}
else
System::Console::WriteLine("Array value: {0}", x[Item]);
}
};
void f(DataCollection y, int Item) {
y.f(Item);
}
int main() {
DataCollection ^ a = gcnew DataCollection(10);
f(*a, 7); // dereference a handle, return handle's object
(*a).f(11); // access member via dereferenced handle
}
Array value: 7
Cannot access array element 11, size is 10
このサンプルでは、ネイティブ参照 (&
) をマネージド型の int
メンバーにバインドできないことを示しています。これは、ガベージ コレクトされたヒープに int
が格納される可能性があり、またネイティブ参照がマネージド ヒープ上のオブジェクトの移動を追跡しないためです。 解決策は、ローカル変数を使用すること、または、&
を %
に変更して、追跡参照にすることです。
// mcppv2_handle_5.cpp
// compile with: /clr
ref struct A {
void Test(unsigned int &){}
void Test2(unsigned int %){}
unsigned int i;
};
int main() {
A a;
a.i = 9;
a.Test(a.i); // C2664
a.Test2(a.i); // OK
unsigned int j = 0;
a.Test(j); // OK
}
要件
コンパイラ オプション: /clr