关于浅拷贝和深拷贝

说明:此文根据《编写高质量代码改善C#的57个建议》的建议14而写。在自己根据作者的思路进行代码练习的时候,感觉作者并未把定义说清楚全面,于是在定义中加入了自己的理解部分,如定义中的绿色字体部分。

为对象创建副本的技术成为拷贝(或叫克隆)。我们将拷贝分为浅拷贝和深拷贝。

  • 浅拷贝:将对象中的所有字段复制到新的对象(此对象为源对象的一个副本,即新对象)中。其中,值类型字段的值复制到副本中后,在副本中的修改不会影响源对象对应的值,在源对象中的修改不会影响到副本(新对象)对应的值。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身,在源对象中对引用类型的字段值做修改会影响到副本(新对象)本身。
  • 深拷贝:将对象中的所有字段复制到新的对象中。不过,无论对象的字段是值类型字段还是引用类型字段,都会重新创建并复制,对副本的修改不会影响到源对象本身,对源对象的修改不会影响到副本本身。

浅拷贝实现:

class Employee : ICloneable

{

public string IDCode { get; set; }

public int Age { get; set; }

public Department Department { get ; set ; }

public object Clone()

{

return this.MemberwiseClone();

}

}

class Department

{

public string Name { get; set; }

public override string ToString()

{

return this.Name;

}

}

若调用代码为:

static void Main(string [] args)

{

Employee mike = new Employee ()

{

IDCode = "NB123" ,

Age = 30,

Department = new Department () { Name = "Dep1" }

};

Employee rose = mike.Clone() as Employee ;

mike.IDCode = "NB456" ;

mike.Age = 60;

mike.Department.Name = "Dep2" ;

Console .WriteLine("mike :" );

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("rose :" );

Console .WriteLine(rose.IDCode);

Console .WriteLine(rose.Age);

Console .WriteLine(rose.Department);

Console .ReadKey();

}

则输出结果为:

此结果验证了定义中:值类型字段在源对象中的修改不会影响到副本(新对象)对应的值,引用类型的字段在源对象中对引用类型的字段值做修改会影响到副本(新对象)本身

若调用代码为:

static void Main(string [] args)

{

Employee mike = new Employee ()

{

IDCode = "NB123" ,

Age = 30,

Department = new Department () { Name = "Dep1" }

};

Employee rose = mike.Clone() as Employee ;

rose.IDCode = "NB456" ;

rose.Age = 60;

rose.Department.Name = "Dep2" ;

Console .WriteLine("mike :" );

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("rose :" );

Console .WriteLine(rose.IDCode);

Console .WriteLine(rose.Age);

Console .WriteLine(rose.Department);

Console .ReadKey();

}

则输出结果为:

此结果验证了定义中:值类型字段在副本中的修改不会影响源对象对应的值,引用类型的字段在在副本中对引用类型的字段值做修改会影响到源对象本身。

注意到Employee 的IDCode字段是string 类型。理论上来说string类型是引用类型,但是由于该引用类型的特殊性,Object.MemberwiseClone()仍旧为其创建了副本,也就是说在浅拷贝中,我们应将string类型当作是值类型。

深拷贝实现:

深拷贝有多种实现方法,最简单的方法是手动对字段逐个进行赋值,但是这种方法容易出错,也就是说,如果类型的字段发生变化或增减,那么该拷贝方法也要发生相应的变化,所以建议使用序列化的形式来进行深拷贝。实现如下:

需添加如下三个命名空间:

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

using System.Runtime.Serialization;

[ Serializable]

class Employee : ICloneable

{

public string IDCode { get; set; }

public int Age { get; set; }

public Department Department { get ; set ; }

public object Clone()

{

using( Stream sr= new MemoryStream ())

{

IFormatter formatter = new BinaryFormatter ();

formatter.Serialize(sr, this);

sr.Seek(0, SeekOrigin .Begin);

return formatter.Deserialize(sr) as Employee ;

}

}

}

[ Serializable]

class Department

{

public string Name { get; set; }

public override string ToString()

{

return this.Name;

}

}

若调用代码为:

static void Main(string [] args)

{

Employee mike = new Employee ()

{

IDCode = "NB123" ,

Age = 30,

Department = new Department () { Name = "Dep1" }

};

Employee rose = mike.Clone() as Employee ;

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("开始改变rose值:" );

rose.IDCode = "NB456" ;

rose.Age = 60;

rose.Department.Name = "Dep2" ;

Console .WriteLine("mike :" );

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("rose :" );

Console .WriteLine(rose.IDCode);

Console .WriteLine(rose.Age);

Console .WriteLine(rose.Department);

Console .ReadKey();

}

输出结果:

此结果验证了定义中:对副本的修改不会影响到源对象本身

若调用代码为:

static void Main(string [] args)

{

Employee mike = new Employee ()

{

IDCode = "NB123" ,

Age = 30,

Department = new Department () { Name = "Dep1" }

};

Employee rose = mike.Clone() as Employee ;

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("开始改变mike值:" );

mike.IDCode = "NB456" ;

mike.Age = 60;

mike.Department.Name = "Dep2" ;

Console .WriteLine("mike :" );

Console .WriteLine(mike.IDCode);

Console .WriteLine(mike.Age);

Console .WriteLine(mike.Department);

Console .WriteLine("rose :" );

Console .WriteLine(rose.IDCode);

Console .WriteLine(rose.Age);

Console .WriteLine(rose.Department);

Console .ReadKey();

}

则输出结果为:

此结果验证了定义中:对源对象的修改不会影响到副本本身

在同一类中同时实现深拷贝和浅拷贝

修改Employee 类即可,调用深拷贝用方法DeepClone(),调用浅拷贝用方法ShallowClone()

[ Serializable]

class Employee : ICloneable

{

public string IDCode { get; set; }

public int Age { get; set; }

public Department Department { get ; set ; }

public object Clone()

{

return this.MemberwiseClone();

}

public Employee DeepClone()

{

using ( Stream sr = new MemoryStream ())

{

IFormatter formatter = new BinaryFormatter ();

formatter.Serialize(sr, this);

sr.Seek(0, SeekOrigin .Begin);

return formatter.Deserialize(sr) as Employee ;

}

}

public Employee ShallowClone()

{

return Clone() as Employee ;

}

}

时间: 2024-12-28 02:47:36

关于浅拷贝和深拷贝的相关文章

js对象浅拷贝和深拷贝详解

js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var

渐析java的浅拷贝和深拷贝

作者: chenssy 出处: http://www.cnblogs.com/chenssy/ 首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值.这个方式称为深拷贝 也就是说浅拷贝只复制一个对象,传递引用,不能复制实例.而深拷贝对对象内部的引用均复制,它是创建一个新的实例,并且复制实例. 对于浅拷贝

正确实现浅拷贝和深拷贝

为对象创建副本的技术称为拷贝(也叫克隆).我们将拷贝分为浅拷贝和深拷贝. 浅拷贝:将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值.而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身. 深拷贝:同样,将对象中的所有字段复制到新的对象中.不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身. 无论是浅拷贝还

深入浅拷贝和深拷贝。初学者的笔记

浅拷贝和深拷贝都是相对拷贝构造函数来讲的 下面代码实现浅拷贝, 1 #include <stdio.h> 2 #include <iostream> 3 #include <string> 4 using namespace std; 5 6 class A 7 { 8 public : 9 A(int a=0):m_num(a){}; 10 A(const A &arr); 11 int set_num(); 12 private: 13 int m_num;

编写高质量代码改善C#程序的157个建议——建议14: 正确实现浅拷贝和深拷贝

建议14: 正确实现浅拷贝和深拷贝 为对象创建副本的技术称为拷贝(也叫克隆).我们将拷贝分为浅拷贝和深拷贝. 浅拷贝 将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值.而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身. 深拷贝 同样,将对象中的所有字段复制到新的对象中.不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不

C++拷贝构造函数(浅拷贝、深拷贝)

下面举一个简单的例子说明对象之间的拷贝(此例中没有自定义拷贝构造函数,在调用拷贝构造函数的时候,编译器会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝)浅拷贝: #include<iostream> using namespace std; class CExample { private:int a; public: CExample(int b) { a=b; } void Show() { cout<<a<<endl; } }; int main(

浅拷贝,深拷贝---ios

#import <Foundation/Foundation.h> @interface Father : NSObject <NSCopying,NSMutableCopying> @property (nonatomic,copy) NSString *name; @property (nonatomic,retain) NSNumber *age; -(id) initWithName:(NSString *)name withAge:(NSNumber *) age; @e

C# 浅拷贝与深拷贝区别

浅拷贝:给对象拷贝一份新的对象.浅拷贝的定义—— 只对值类型(或string)类型分配新的内存地址.深拷贝:给对象拷贝一份全新的对象.深拷贝的定义—— 对值类型分配新的内存地址,引用类型.以及引用类型的内 http://blog.csdn.net/lai123wei/article/details/7217365 C# 浅拷贝与深拷贝区别,布布扣,bubuko.com

Python之美[从菜鸟到高手]--浅拷贝、深拷贝完全解读(copy源码分析)

可悲的我一直以为copy模块是用C写的,有时候需要深入了解deepcopy,文档描述的实在太简单,还是不知所云. 比如说最近看sqlmap源码中AttribDict的_deepcopy__有些疑惑, def __deepcopy__(self, memo): retVal = self.__class__() memo[id(self)] = retVal for attr in dir(self): if not attr.startswith('_'): value = getattr(se

opencv 浅拷贝与深拷贝迷惑之处

关于opencv中的浅拷贝与深拷贝大家众所周知,这里先赘述一下 (1)浅拷贝: Mat B; B = image  // 第一种方式 Mat C(image); // 第二种方式 这两种方式称为浅copy,是由于它们有不同的矩阵头,但是它们共享内存空间,即指向一个矩阵.当图像矩阵发生变化时,两者相关联,都会变化. (2)深拷贝 Mat B,C; B = image.clone();       // 第一种方式 image.copyTo(C); // 第二种方式 深拷贝是真正的copy了一个新的