国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

實例解析C++/CLI的“克隆”

2019-11-17 05:32:51
字體:
來源:轉載
供稿:網友

  C++/CLI不但支持基于堆棧的對象,同時也支持基于堆的對象;然而,假如想與其他基于CLI的語言(如C#、J#、Visual Basic)進行互操作的話,必須要清楚地知道,這些語言只支持基于堆的對象;當處于基于堆的對象環境中時,你與對象之間,永遠只有"一臂之遙",比方說,兩個給定的句柄h1與h2,只有在為這種句柄類型定義了相應的賦值操作符時,*h1 = *h2才會工作正常,而對C++/CLI之外的其他語言中的類型來說,情況可能就不是這樣了。同樣地,一個遵從CLS的機制需要創建對象的一份副本,這種機制被稱為"克隆"。

  使用CLI庫中的Clone函數

  請看例1中的代碼,其使用了類似于矢量的一個System::ArrayList類,插1是程序的輸出。

  例1:

using namespace System;
using namespace System::Collections;

void PRintEntries(String^ s, ArrayList^ aList);

int main()
{
 ArrayList^ al1 = gcnew ArrayList;
 /*1*/ al1->Add("Red");
 al1->Add("Blue");
 al1->Add("Green");
 al1->Add("Yellow");
 /*2*/ PrintEntries("al1", al1);
 /*3*/ ArrayList^ al2 = static_cast<ArrayList^>(al1->Clone());
 /*4*/ PrintEntries("al2", al2);
 /*5*/ al1->Remove("Blue");
 al1->Add("Black");
 al1->RemoveAt(0);
 al1->Insert(0, "Brown");

 /*6*/ PrintEntries("al1", al1);
 /*7*/ PrintEntries("al2", al2);
}
void PrintEntries(String^ s, ArrayList^ aList)
{
 Console::Write("{0}: ", s);
 for each(Object^ o in aList)
 {
  Console::Write("/t{0}", o);
 }
 Console::WriteLine();
}
  插1:程序輸出

al1: Red Blue Green Yellow
al2: Red Blue Green Yellow
al1: Brown Green Yellow Black
al2: Red Blue Green Yellow
  ArrayList al1由4個代表不同顏色的字符串組成,通過在標記3中調用ArrayList::Clone函數,可以對此對象作一個完整的復制,所以,標記2與4表示的輸出完全相同。

  接下來,從al1中移除了第二個元素,在末尾加入了一個新的元素,并修改了第一個元素的值。當把標記6與7表示的輸出進行一個對比時,你會發現,對al1所作的修改,完全不會影響到al2。在此需要說明的是,al2內部的引用,指向其自身元素的私有副本,而不是al1中的元素,這就是通常提到的"深拷貝",反之,只是簡單地把兩個ArrayList內部引用指向同一個值集(如al2=al1的賦值操作),這稱為"淺拷貝"。

  也就是說,假如你希望復制所擁有的對象,應該參照庫函數Clone機制中的復制過程。

  在類型中添加克隆

  克隆的要害是實現System::ICloneable標準接口,其需要你定義一個調用Clone、不接受任何參數、并帶有一個System::Object^返回類型的函數,返回的句柄指向一個新的對象,這個對象是被調用對象的一個副本。請看例2:

  例2:

public ref class Point : ICloneable
{
 // ...
 public:
  virtual Object^ Clone()
  {
   return MemberwiseClone();
  }
};

int main()
{
 /*1*/ Point^ p1 = gcnew Point(3, 5);
 /*2*/ Console::WriteLine("p1: {0}", p1);
 /*3*/ Point^ p2 = static_cast<Point^>(p1->Clone());
 /*4*/ p1->Move(9, 11);
 /*5*/ Console::WriteLine("p1: {0}", p1);
 /*6*/ Console::WriteLine("p2: {0}", p2);
}
  以下是程序的輸出:

p1: (3,5)
p1: (9,11)
p2: (3,5)
  在標記3中,通過調用Clone進行了復制,而因為此函數返回一個Object^類型的值(在此為一個Point的引用),在把它賦值給p2之前,必須轉換為一個Point^。(即便Point::Clone真的返回一個Point的句柄,也不能這樣聲明函數,因為不符合接口規范。)

  在類型System::Object中定義了一個名為MemberwiseClone的函數,如下所示:

protected:
Object^ MemberwiseClone();
  這個函數創建并返回對象的一份副本,而一般的用法是,對任意句柄x,以下的表達式都為真:

x->MemberwiseClone() != x
x->MemberwiseClone()->GetType() == x->GetType()
  通常來說,復制一個對象必須創建對象的一個新實例,但同時也可能需要對內部數據結構進行復制,在此不需要調用任何的構造函數。
Object::MemberwiseClone執行一個具體而精確的克隆操作。它創建類對象的一個新實例,并用源對象字段內容,初始化對應的所有字段,就似乎在賦值;但是要注重的是,字段內容本身并沒有被克隆,所以,這個函數執行的是一個對象的"淺拷貝"。

  在Point的實現中,使用了兩個實例變量,兩者均具有基本類型int。基本類型就是值類型,所以對一個Point的淺拷貝已經完全能滿足我們的需要,也就是通過調用基類對象的MemberwiseClone來完成的。

  下面來看一個Circle類,其包含了一個指向Point的句柄(表示原始位置)和一個基本類型字段(表示radius半徑);見例3:

  例3:


using namespace System;

public ref class Circle : ICloneable
{
 Point^ origin;
 float radius;
 public:
  property Point^ Origin
  {
   Point^ get() { return static_cast<Point^>(origin->Clone()); }
  }
  void SetOrigin(int x, int y)
  {
   origin->X = x;
   origin->Y = y;
  }
  void SetOrigin(Point^ p)
  {
   SetOrigin(p->X, p->Y);
  }
  property double Radius
  {
   double get() { return radius; }
   void set(double value) {
    radius = static_cast<float>(value);
   }
  }
  Circle()
  {
   origin = gcnew Point;
   SetOrigin(0, 0);
   Radius = 0.0;
  }
  Circle(int x, int y, double r)
  {
   origin = gcnew Point;
   SetOrigin(x, y);
   Radius = r;
  }
  Circle(Point^ p, double r)
  {
   origin = gcnew Point;
   SetOrigin(p->X, p->Y);
   Radius = r;
  }
  virtual String^ ToString() override
  {
   return String::Concat("{", Origin, ",", Radius, "}");
  }

  virtual Object^ Clone()
  {
   /*1*/ Circle^ c = static_cast<Circle^>(MemberwiseClone());
   /*2*/ c->origin = static_cast<Point^>(origin->Clone());
   /*3*/ return c;
  }
};
  Circle類中Origin屬性的get方法由Point::Clone來實現,如定義中所示,其返回一個Point中心點的Point副本。

  在標記1中,調用了Object::MemberwiseClone以對Circle進行了一次淺拷貝,新Circle中的radius就與當前值一樣了,并且兩個Circle中的origin均引用同一個Point;因此,在標記2中,調用了Point::Clone以確保新Circle的origin引用為一個當前Point中心點的副本;最后,在標記3中,返回了這個新Circle的句柄。例4是測試這個類的程序:

  例4:

int main()
{
 /*1*/ Circle^ c1 = gcnew Circle(5, 9, 1.5);
 /*2*/ Console::WriteLine("c1: {0}", c1);

 /*3*/ Circle^ c2 = static_cast<Circle^>(c1->Clone());

 /*4*/ Point^ p = c1->Origin;
 /*5*/ Console::WriteLine(" p: {0}", p);

 /*6*/ c1->SetOrigin(9, 11);

 /*7*/ Console::WriteLine("c1: {0}", c1);
 /*8*/ Console::WriteLine(" p: {0}", p);
 /*9*/ Console::WriteLine("c2: {0}", c2);
} 克隆數組

  請看例5,其擴展了泛型類Vector以支持克隆。因為所有的CLI數組類型均派生自System::Array,而其也實現了System::ICloneable接口,所以可以對一個數組的引用,簡單地調用Clone以復制數組中的元素,如例子中所示。當然,在轉換中也必須包括數組符號。

  例5:

generic <typename T>
public ref class Vector
{
 int length;
 /*1*/ array<T>^ vector;

 public:
  virtual Object^ Clone()
  {
   Vector<T>^ v = static_cast<Vector<T>^>(MemberwiseClone());
   v->vector = static_cast<array<T>^>(vector->Clone());
   return v;
  }

};

int main()
{
 /*1*/ Vector<int>^ v1 = gcnew Vector<int>(5, 7);
 /*2*/ Console::WriteLine("v1: {0}", v1);
 /*3*/ Vector<int>^ v2 = static_cast<Vector<int>^>(v1->Clone());
 /*4*/ Console::WriteLine("v2: {0}", v2);
 /*5*/ v1[0] = 3;
 /*6*/ v1[3] = 9;
 /*7*/ v2[4] = 1;
 /*8*/ Console::WriteLine("v1: {0}", v1);
 /*9*/ Console::WriteLine("v2: {0}", v2);
}
  克隆并派生類

  到目前為止,所演示的方法對直接從Object^派生而來的類來說,都完全正確,然而,對從其他類派生而來的類來說,就不正確了。請看例6,因為是直接從System::Object派生而來,所以它的克隆方法正如前面所看到的那樣。Base被用作基類,而例7中的程序由其派生而來:

  例6:


public ref class Base : ICloneable
{
 array<int>^ bPair ;
 public:
  Base(int i, int j)
  {
   bPair = gcnew array<int>(2) {i, j};
  }
  void SetValue(int i, int j)
  {
   bPair[0] = i;
   bPair[1] = j;
  }
  virtual String^ ToString() override
  {
   return String::Concat("[", bPair[0], ":", bPair[1], "]");
  }
  virtual Object^ Clone() override
  {
   Base^ b = static_cast<Base^>(MemberwiseClone());
   b->bPair = static_cast<array<int>^>(bPair->Clone());
   return b;
  }
};
  例7:

using namespace System;

public ref class Derived : Base, ICloneable
{
 array<int>^ dPair;
 public:
  Derived(int bi, int bj, int i, int j) : Base(bi, bj)
  {
   dPair = gcnew array<int>(2) {i, j};
  }
  void SetValue(int bi, int bj, int i, int j)
  {
   Base::SetValue(bi, bj);
   dPair[0] = i;
   dPair[1] = j;
  }
  virtual String^ ToString() override
  {
   return String::Concat("[{", Base::ToString(), "}", dPair[0], ":", dPair[1], "]");
  }
  virtual Object^ Clone() override
  {
   // Derived^ d = static_cast<Derived^>(Base::MemberwiseClone());
   Derived^ d = static_cast<Derived^>(Base::Clone());
   d->dPair = static_cast<array<int>^>(dPair->Clone());
   return d;
  }
};
  在Derived中顯示聲明實現ICloneable是多余的,因為Derived的基類已經這樣做了。而這個例子中唯一新的東西就是在Clone中調用Base::Clone,這取代了前一個對MemberwiseClone的調用(其已被注釋)。通過調用Base::Clone(其會調用基類的Clone,而它又最終調用Object::MemberwiseClone),這做就得到了一個基類對象的克隆。例8是測試程序。

  例8:

int main()
{
 Derived^ d1 = gcnew Derived(10, 20, 30, 40);
 Console::WriteLine("d1 = {0}", d1);

 Derived^ d2 = static_cast<Derived^>(d1->Clone());
 Console::WriteLine("d2 = {0}", d2);

 d1->Base::SetValue(5, 6);

 Console::WriteLine("d1 = {0}", d1);
 Console::WriteLine("d2 = {0}", d2);
}
  在此,為什么要讓Clone成為一個虛擬函數呢?因為,當調用Base::Clone時,必須確保調用了最恰當的實現,而虛擬函數查找,正是實現此的良方。

  無構造的創建

  一個對Clone的實現,不應調用任何其自身的類構造函數,對大多數類而言,這都不是問題,因為所有它們的構造函數只是初始化非靜態的數據成員,然而,假如一個構造函數初始化了任意的靜態數據成員或執行了其他的操作,這些操作在克隆期間就不會被完成,除非Clone本身執行了這些操作。在例9中,類Test包含了一個靜態計數器,其跟蹤類型對象被創建的數目;標記1與2執行了與構造函數同樣的操作。

  例9:

public ref class Test : ICloneable
{
 int data;
 static int objectCount = 0;
 public:
  Test()
  {
   data = 0;
   ++objectCount;
  }
  Test(int value)
  {
   data = value;
   ++objectCount;
  }
  virtual String^ ToString() override
  {
   return String::Concat(data, ", ", objectCount);
  }
  virtual Object^ Clone()
  {
   /*1*/ Test^ copy = static_cast<Test^>(MemberwiseClone());
   /*2*/ ++objectCount;
   return copy;
  }
};
int main()
{
 /*3*/ Test^ t1 = gcnew Test;
 Console::WriteLine("t1 using new: {0}", t1);
 /*4*/ Test^ t2 = static_cast<Test^>(t1->Clone());
 Console::WriteLine("t2 using Clone: {0}", t2);
 /*5*/ Test^ t3 = gcnew Test(1);
 Console::WriteLine("t3 using new: {0}", t3);
 /*6*/ Test^ t4 = static_cast<Test^>(t3->Clone());
 Console::WriteLine("t4 using Clone: {0}", t4);
}
}
  以下是程序輸出:


t1 using new: 0, 1
t2 using Clone: 0, 2
t3 using new: 1, 3
t4 using Clone: 1, 4
  由輸出可見,當一個對象被構造或被克隆時,對象計數都相應地增長了。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 金昌市| 平远县| 翼城县| 东兴市| 海南省| 永兴县| 长宁区| 祁连县| 南城县| 武冈市| 平武县| 民县| 通许县| 偃师市| 乌拉特后旗| 会宁县| 盱眙县| 乌拉特前旗| 农安县| 石景山区| 八宿县| 甘南县| 荥阳市| 沅江市| 若尔盖县| 杭锦旗| 明光市| 太康县| 平江县| 永新县| 辽阳市| 车险| 庄浪县| 渭南市| 阳东县| 进贤县| 凤凰县| 长葛市| 宁海县| 高平市| 馆陶县|