C++/CLI 同時支援堆疊型與堆積型物件,但 C#、VB.NET 等 CLI 語言僅支援堆積型物件。在堆積環境中,物件存取永遠透過控制代碼(handle)。若要實現物件獨立副本,避免多個控制代碼指向同一實體造成非預期副作用,必須實作「深拷貝」機制——即「克隆」。
System::ArrayList 等集合類別已實作 ICloneable 介面:
ArrayList^ al1 = gcnew ArrayList;
al1->Add("Red"); al1->Add("Blue"); al1->Add("Green"); al1->Add("Yellow");
ArrayList^ al2 = static_cast(al1->Clone()); // 深拷貝
al1->Remove("Blue"); al1->Add("Black"); al1->RemoveAt(0); al1->Insert(0, "Brown");
// al1 變更不影響 al2,證明為深拷貝
淺拷貝(al2 = al1)僅複製參考,雙方指向相同元素集合;深拷貝則建立元素私有副本。
關鍵在於實作 System::ICloneable 介面,定義 virtual Object^ Clone() 方法:
public ref class Point : ICloneable
{
public:
virtual Object^ Clone() { return MemberwiseClone(); }
};
Object::MemberwiseClone 建立新實例並複製所有欄位(淺拷貝)。對於值型別欄位(如 int),淺拷貝即滿足深拷貝需求。
當類別包含參考型別欄位(如 Point^ origin)時,需手動深拷貝該欄位:
public ref class Circle : ICloneable
{
Point^ origin; float radius;
public:
virtual Object^ Clone() override
{
Circle^ c = static_cast(MemberwiseClone()); // 淺拷貝 Circle
c->origin = static_cast(origin->Clone()); // 深拷貝 Point
return c;
}
};
測試程式證明:修改 c1 的 origin 不影響 c2。
所有 CLI 陣列繼承自 System::Array,已實作 ICloneable:
generic
public ref class Vector
{
array^ vector;
public:
virtual Object^ Clone()
{
Vector^ v = static_cast^>(MemberwiseClone());
v->vector = static_cast^>(vector->Clone()); // 含陣列符號轉型
return v;
}
};
衍生類別應呼叫基類 Clone() 而非 MemberwiseClone(),確保基類部分正確克隆:
public ref class Derived : Base, ICloneable
{
virtual Object^ Clone() override
{
Derived^ d = static_cast(Base::Clone()); // 呼叫基類 Clone
d->dPair = static_cast^>(dPair->Clone());
return d;
}
};
Clone 宣告為 virtual 確保多型呼叫正確性。
克隆不應呼叫建構函式。若類別有靜態計數器追蹤物件數量,需在 Clone 中手動遞增:
public ref class Test : ICloneable
{
static int objectCount = 0;
virtual Object^ Clone()
{
Test^ copy = static_cast(MemberwiseClone());
++objectCount; // 手動同步靜態計數
return copy;
}
};
輸出證明:建構與克隆皆正確遞增計數器。