物件控制代碼運算子 (^) (C++/CLI 和 C++/CX)
「控制代碼宣告子」(^
,唸成 "hat") 會修改型別指定名稱,以表示當系統判斷不再存取宣告的物件時,應自動刪除該物件。
存取宣告的物件
以控制代碼宣告子宣告的變數作用如同物件指標。 然而,此變數指向整個物件,無法指向物件的成員,也不支援指標算術。 使用間接取值運算子 (*
) 存取物件,使用箭號成員存取運算子 (->
) 存取物件的成員。
Windows 執行階段
編譯器會使用 COM「參考計數」機制,來判斷物件是否不再使用且可刪除。 究其原因是,從 Windows 執行階段介面衍生的物件實際上是 COM 物件。 當物件建立或複製時參考計數遞增,當物件設定為 null 或超出範圍時則遞減。 如果參考計數歸零,物件會自動並立即刪除。
控制代碼宣告子的優點是,在 COM 中,必須明確地管理物件的參考計數 (這個程序相當繁瑣且易錯), 也就是,若要遞增和遞減參考計數,必須呼叫物件的 AddRef() 和 Release() 方法。 不過,如果您利用控制代碼宣告子來宣告物件,編譯器就會產生可自動調整參考計數的程式碼。
如需如何將物件具現化的詳細資訊,請參閱 ref new。
需求
編譯器選項:/ZW
通用語言執行平台
系統會使用 CLR「記憶體回收行程」機制,來判斷物件是否不再使用且可刪除。 Common Language Runtime 會維持用以配置物件的堆積,並使用您程式中的 Managed 參考 (變數),指出物件在堆積上的位置。 當物件不再使用時,物件在堆積上佔用的記憶體會被釋放。 記憶體回收行程會定期壓縮堆積,以更有效地利用釋放的記憶體。 壓縮堆積可以在堆積上移動物件,而使得受控參考所參考的位置無效。 然而,記憶體回收行程知道所有 Managed 參考的位置,而且會自動更新以指出物件在堆積上目前的位置。
因為原生 C++ 指標 (*
) 和參考 (&
) 不是 Managed 參考,所以記憶體回收行程無法自動更新它們所指的位址。 若要解決這個問題,請使用控制代碼宣告子,指定記憶體回收行程知道且會自動更新的變數。
如需詳細資訊,請參閱 如何:在原生類型中宣告句柄。
範例
這個範例示範如何在 Managed 堆積上建立參考型別的執行個體。 這個範例也示範如何以一個控制代碼初始化另一個控制代碼,導致在 Managed、記憶體回收堆積上同一個物件有兩個參考。 請注意,將 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
下列範例示範如何在 Managed 堆積上宣告物件控制代碼,而物件的型別為 Boxed 實值型別。 這個範例也會示範如何從 Boxed 物件取得實值型別。
// 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
Managed 類型的成員,因為 int
可能會儲存在垃圾收集堆積中,而原生參考不會追蹤 Managed 堆積中的物件移動。 解決方法是使用區域變數,或將 &
變更為 %
,使其成為追蹤參考。
// 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