动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > C/C++教程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ C/C++教程 ]的信息

本月文章推荐
.C语言初学者入门讲座 第十讲 函数.
.打字游戏.
.一个非常简单的题.
.怎样学VC与我为何选择C/C++.
.C++中的健壮指针和资源管理.
.第四篇:达到目标必经的路 之二.
.解决网爬工具爬取页面信息出现乱.
.Windows文件保护的应用.
.VC命名管道通信的实现(组图).
.关于全排列算法,大家请指导!.
.网际幕後的无名英雄----IP 原理实.
.创建和使用库:静态、共享和动态.
.C程序设计例解.
.浅谈C++中的内存管理.
.nsd启动.
.用户界面设计风格说明下.
.C++远程关机API的学习过程.
.C语言中的面向对象思想.
.JDOM使用详解及实例.
.1.2 算法思想.

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

发表日期:2008-3-8 |



  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
  由输出可见,当一个对象被构造或被克隆时,对象计数都相应地增长了。
上一篇:使用C++和XML建立智能文档(三) 人气:423
下一篇:试一下 moving ball 人气:321
浏览全部C/C++的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-10-12 team论坛 v2.0.4 bulid 080916 A
2008-10-12 Roclog v3.1.6
2008-10-12 SupeV v1.0.1 简体中文 GBK
2008-10-12 NetCMS v1.6.0.1010 正式版
2008-10-12 PHP考试系统PPFrame v1.2.7
2008-10-12 LPAS个人相册 v1.6.3
2008-10-12 快问仿百度知道系统 动态-静态-互
2008-10-12 方卡广告防点击系统 V1.0 GB2312
2008-10-12 泡菜内容管理系统[PCMS] v1.0 Bu
2008-10-11 联系人分组工具 v1.1 中文破解版
2008-10-11 FaceMelter变脸 v2.0 汉化破解版
2008-10-11 PathTracker道路跟踪仪 v1.2 破解
2008-10-11 Rooms手机聊天室 v0.6.7 破解版
2008-10-11 RemoteDesktop远程桌面 v1.0 破解
2008-10-11 ProRemote远程调音台 v1.0.1 破解
2008-10-11 PicShare照片共享 v1.0.0 破解版
2008-10-11 Photogene照片编辑器 v1.5 汉化破
2008-10-11 WriteRoom共享文档 v1.0 破解版
  发表评论
姓 名: 验证码:
内 容:
站长工具:网站收录查询 | Google PR查询 | ALEXA排名查询 | CSS在线编辑器 | 广告代码 | js/vbs加密 | md5加密 | 进制转换 | UTF-8 转换工具 | Html转换js | Html转换asp | Html转换php | Html转换perl
实用工具:汉字翻译拼音 | 拼音字典 | 符号对照表 | 个税计算 | 实时汇率查询换算 | 经典小工具 | 汉字简繁转换 | 普通单位换算 | 公制单位换算 | 生辰老黄历 | 国内电话区号 | 国家代码与域名缩写 | 文字加密解密 | 健康查询 | 万年历 | 汉字横竖排版 | 手机号码查询 | 计算器 | ip搜索
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号
ホームページ制作 不動産検索システム 求人情報
防水工事·改修工事 フットサル大会 探偵
SEO対策 中国語教室 ホームページ作成