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