实例理会C++/CLI的“克隆”
当前位置:以往代写 > C/C++ 教程 >实例理会C++/CLI的“克隆”
2019-06-13

实例理会C++/CLI的“克隆”

副标题#e#

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();
}


#p#副标题#e#

插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);
}

#p#副标题#e#

以下是措施的输出:

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执行一个具体而准确的克隆操纵。它建设类工具的一个新实例,并用源工具字段内容,初始化对应的所有字段,就仿佛在赋值;可是要留意的是,字段内容自己并没有被克隆,所以,这个函数执行的是一个工具的"浅拷贝"。

#p#分页标题#e#

在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;
   }
};

#p#副标题#e#

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);
}

    关键字:

在线提交作业