深拷贝和浅拷贝之间有什么区别?

深拷贝和浅拷贝之间有什么区别?

浅拷贝重复尽可能less。 集合的浅表副本是集合结构的副本,而不是元素。 用浅拷贝,现在两个集合共享单个元素。

深拷贝复制一切。 集合的深层副本是两个集合,其中原始集合中的所有元素都是重复的。

宽度与深度; 根据与您的对象作为根节点的引用树来思考。

浅:

在复制之前浅拷贝完成

variablesA和B指的是内存的不同区域,当B分配给A时,这两个variables指的是相同的内存区域。 以后对其中任一内容的修改会立即反映在其他内容中,因为它们共享内容。

深:

在复制之前深度复制深刻的完成

variablesA和B指的是不同的存储区,当B被分配给A时,A指向的存储区中的值被复制到B指向的存储区中。 后来对内容的修改仍然是A或B所独有的; 内容不共享。

总之,这取决于什么点。 在浅拷贝中,对象B指向对象A在内存中的位置。 在深度复制中,对象A的内存位置中的所有内容都被复制到对象B的内存位置。

这个wiki文章有一个很好的图表。

http://en.wikipedia.org/wiki/Object_copy

特别是对于iOS开发者:

如果BA浅拷贝 ,那么对于原始数据就像B = [A assign]; 对象就像B = [A retain] ;

B和A指向相同的内存位置

如果BA深层副本 ,那么它就像B = [A copy];

B和A指向不同的内存位置

B存储器地址与A相同

B与A的内容相同

浅拷贝:将一个对象的成员值复制到另一个对象中。

深度复制:将一个对象的成员值复制到另一个对象中。
任何指针对象都被复制并被深度复制。

例:

 class String { int size; char* data; }; String s1("Ace"); // s1.size = 3 s1.data=0x0000F000 String s2 = shallowCopy(s1); // s2.size =3 s2.data = 0X0000F000 String s3 = deepCopy(s1); // s3.size =3 s3.data = 0x0000F00F // (With Ace copied to this location.) 

尝试考虑下面的图像

在这里输入图像描述

例如Object.MemberwiseClone创build一个拷贝链接

并使用ICloneable接口,您可以获得深层复制如下所述

我还没有在这里看到一个简短易懂的答案 – 所以我会试试看。

对于浅拷贝,源指向的任何对象也指向目的地(这样就不会复制引用的对象)。

使用深层复制时,将复制源指向的任何对象,并将复制指向目标(因此每个引用对象将有2个)。 这会recursion下来的对象树。

{想象两个对象:A和B是相同types的_t(就C ++而言),而你正在考虑浅/深拷贝A到B}

浅拷贝:简单地将A的引用复制到B中。将其视为A的地址副本。 所以,A和B的地址将是相同的,即他们将指向相同的内存位置,即数据内容。

深层复制:只需复制A的所有成员,为B分配不同位置的内存,然后将复制的成员分配给B以实现深度复制。 这样,如果A变得不存在,B在内存中仍然有效。 使用正确的术语将是克隆,在那里你知道它们是完全相同的,但是不同的(即作为存储空间中的两个不同的实体存储)。 您还可以提供您的克隆包装,您可以通过包含/排除列表决定在深度复制过程中select哪些属性。 创buildAPI时,这是相当普遍的做法。

你可以select做一个浅的副本ONLY_IF你明白所涉及的赌注。 当你用C ++或C处理大量的指针时,做一个对象的浅拷贝是一个坏主意。

EXAMPLE_OF_DEEP COPY_例如,当您尝试进行image processing和对象识别时,您需要在处理区域之外屏蔽“不相关和重复运动”。 如果你正在使用图像指针,那么你可能有规范来保存这些蒙版图像。 现在…如果你做了一个图像的浅层副本,当指针引用从堆栈中杀死,你失去了参考和它的副本,即将有一个访问冲突的运行时错误在某个时刻。 在这种情况下,你需要的是通过克隆你的图像的深层副本。 通过这种方式,您可以检索面膜,以防将来需要它们。

EXAMPLE_OF_SHALLOW_COPY与StackOverflow中的用户相比,我并不是非常有知识的人,所以随时删除这个部分,并且如果能够澄清,就举个很好的例子。 但是我真的认为,如果你知道你的程序要运行一段无限的时间,也就是在函数调用的堆栈上进行连续的“push-pop”操作,那么这样做不是一个好主意。 如果你正在向业余或新手展示某些东西(例如C / C ++教程的东西),那么可能是好的。 但是,如果您正在运行诸如监视和检测系统或声纳追踪系统之类的应用程序,则不应该在周围继续浅显地复制物体,因为这会迟早地破坏您的程序。

 char * Source = "Hello, world."; char * ShallowCopy = Source; char * DeepCopy = new char(strlen(Source)+1); strcpy(DeepCopy,Source); 

“ShallowCopy”指向与“源”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。

只是为了方便理解,您可以按照以下文章进行操作: https : //www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm


浅拷贝:

浅拷贝


深度复制:

深度复制

什么是浅拷贝?

浅拷贝是一个对象的按位拷贝。 创build一个新对象,其中包含原始对象中值的精确副本。 如果对象的任何字段是对其他对象的引用,则只复制引用地址,即只复制内存地址。 浅拷贝

在这个图中, MainObject1具有inttypes的字段field1ContainObject1types的ContainObject 。 当您执行MainObject1的浅拷贝时,将使用包含field1的复制值并仍指向ContainObject1本身的field2来创buildContainObject1 。 请注意,因为field1是基本types,所以它的值被复制到field2但由于ContainedObject1是一个对象, MainObject2仍然指向ContainObject1 。 因此,对ContainObject1中的MainObject1所做的任何更改都将反映在MainObject2

现在如果这是浅拷贝,让我们看看是什么深拷贝?

什么是深度复制?

深层副本将复制所有字段,并复制由这些字段指向的dynamic分配的内存。 当一个对象与它引用的对象一起复制时,会发生深度复制。 深度复制

在该图中,MainObject1具有inttypes的字段field1ContainObject1types的ContainObject 。 当您执行MainObject1的深层副本时,将使用包含复制的field1值和包含复制的ContainObject1 field2来创buildContainObject1 。 请注意对ContainObject1中的MainObject1所做的任何更改都不会反映在MainObject2

好文章

在面向对象编程中,一个types包含一组成员字段。 这些字段可以通过值或引用存储(即指向值的指针)。

在浅拷贝中,创build一个新types的实例,并将值复制到新实例中。 引用指针也像值一样被复制。 因此,引用指向原始对象。 由引用存储的成员的任何更改都显示在原始和副本中,因为没有对引用的对象进行复制。

在深度复制中,按照以前的方式复制按值存储的字段,但不复制通过引用存储的对象的指针。 相反,深拷贝是由被引用的对象组成的,并且存储了一个指向新对象的指针。 对这些引用对象所做的任何更改都不会影响对象的其他副本。

“ShallowCopy”指向与“源”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。

 var source = { firstName="Jane", lastname="Jones" }; var shallow = ShallowCopyOf(source); var deep = DeepCopyOf(source); source.lastName = "Smith"; WriteLine(source.lastName); // prints Smith WriteLine(shallow.lastName); // prints Smith WriteLine(deep.lastName); // prints Jones 
 struct sample { char * ptr; } void shallowcpy(sample & dest, sample & src) { dest.ptr=src.ptr; } void deepcpy(sample & dest, sample & src) { dest.ptr=malloc(strlen(src.ptr)+1); memcpy(dest.ptr,src.ptr); } 

在简单的术语中,浅拷贝类似于通过引用的呼叫,而深拷贝类似于按值呼叫

在“通过引用调用”中,函数的forms参数和实际参数都指向相同的内存位置和值。

在“按值调用”中,函数的forms参数和实际参数都是指不同的内存位置,但具有相同的值。

浅拷贝 – 原始和浅拷贝对象中的引用variables具有对普通对象的引用。

深度复制 – 原始和深度复制对象内的引用variables具有对不同对象的引用。

克隆总是做浅拷贝。

 public class Language implements Cloneable{ String name; public Language(String name){ this.name=name; } public String getName() { return name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } 

主要类是以下 –

 public static void main(String args[]) throws ClassNotFoundException, CloneNotSupportedException{ ArrayList<Language> list=new ArrayList<Language>(); list.add(new Language("C")); list.add(new Language("JAVA")); ArrayList<Language> shallow=(ArrayList<Language>) list.clone(); //We used here clone since this always shallow copied. System.out.println(list==shallow); for(int i=0;i<list.size();i++) System.out.println(list.get(i)==shallow.get(i));//true ArrayList<Language> deep=new ArrayList<Language>(); for(Language language:list){ deep.add((Language) language.clone()); } System.out.println(list==deep); for(int i=0;i<list.size();i++) System.out.println(list.get(i)==deep.get(i));//false } 

输出以上将会─

假真实的

假假假

在原始对象中做出的任何改变都会反映在浅物体中而不是深物体上。

  list.get(0).name="ViSuaLBaSiC"; System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName()); 

OutPut- ViSuaLBaSiC C

想象一下,有两个数组叫arr1和arr2。

 arr1 = arr2; //shallow copy arr1 = arr2.clone(); //deep copy 

深度复制

深层副本将复制所有字段,并复制由这些字段指向的dynamic分配的内存。 当一个对象与它引用的对象一起复制时,会发生深度复制。

浅拷贝

浅拷贝是一个对象的按位拷贝。 创build一个新对象,其中包含原始对象中值的精确副本。 如果对象的任何字段是对其他对象的引用,则只复制引用地址,即只复制内存地址。

深度复制和吞服复制示例

采取[博客]: http : //sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

深度复制涉及使用一个对象的内容来创build同一个类的另一个实例。 在深层复制中,这两个对象可能包含相同的信息,但目标对象将拥有自己的缓冲区和资源。 任何物体的破坏都不会影响剩余的物体。 重载的赋值操作符将创build对象的深层副本。

浅拷贝涉及将一个对象的内容拷贝到同一个类的另一个实例中,从而创build镜像。 由于直接复制引用和指针,这两个对象将共享另一个对象的外部包含的内容,这是不可预知的。

说明:

使用复制构造函数,我们只需按成员复制数据值成员。 这种复制方法称为浅拷贝。 如果对象是一个简单的类,由内置的types和没有指针组成,这是可以接受的。 此函数将使用值和对象,其行为不会被浅拷贝改变,只有成员指针的地址被复制,而不是地址指向的值。 然后该对象的数据值将被该函数无意中改变。 当函数超出作用域时,包含所有数据的对象的副本将从堆栈popup。

如果对象有任何指针,则需要执行深度复制。 使用对象的深层副本,内存被分配给自由存储中的对象,并且指向的元素被复制。 深度复制用于从函数返回的对象。

要添加更多的其他答案,

  • 对象的浅拷贝根据基于值types的属性的值执行拷贝,并且通过引用来拷贝基于引用types的属性。
  • 对象的深层副本按值执行基于值types的属性的复制,以及按层次结构(引用types)中基于引用types的属性的值进行复制

浅层克隆中 ,只克隆对象的基本属性,但其引用types不被克隆。

深度克隆中 ,克隆对象的基本types和引用types。

复制音乐:

数组是一个类,这意味着它是引用types,所以array1 = array2会产生两个引用相同数组的variables。

但看看这个例子:

  static void Main() { int[] arr1 = new int[] { 1, 2, 3, 4, 5 }; int[] arr2 = new int[] { 6, 7, 8, 9, 0 }; Console.WriteLine(arr1[2] + " " + arr2[2]); arr2 = arr1; Console.WriteLine(arr1[2] + " " + arr2[2]); arr2 = (int[])arr1.Clone(); arr1[2] = 12; Console.WriteLine(arr1[2] + " " + arr2[2]); } 

浅克隆意味着只复制由克隆arrays表示的内存。

如果数组包含值types对象,则值被复制 ;

如果数组包含引用types,则只复制引用 – 因此,有两个数组的成员引用相同的对象

要创build一个复制的深层复制参考types,您必须遍历数组并手动复制每个元素。

拷贝是创build一个新的对象,然后将当前对象的非静态字段复制到新的对象。 如果一个字段是一个值types – >该字段的位拷贝被执行; 对于引用types – >引用被复制,但引用的对象不是; 因此,原始对象及其克隆指的是同一个对象。

深层副本正在创build一个新对象,然后将当前对象的非静态字段复制到新对象。 如果一个字段是一个值types – >该字段的位拷贝被执行。 如果一个字段是引用types – >引用对象的新副本被执行。 要克隆的类必须标记为[Serializable]。

浅拷贝不会创build新的引用,但深拷贝将创build新的引用。 这里是解释深层和浅层复制的程序

 public class DeepAndShollowCopy { int id; String name; List<String> testlist = new ArrayList<>(); /* // To performing Shallow Copy // Note: Here we are not creating any references. public DeepAndShollowCopy(int id, String name, List<String>testlist) { System.out.println("Shallow Copy for Object initialization"); this.id = id; this.name = name; this.testlist = testlist; } */ // To performing Deep Copy // Note: Here we are creating one references( Al arraylist object ). public DeepAndShollowCopy(int id, String name, List<String> testlist) { System.out.println("Deep Copy for Object initialization"); this.id = id; this.name = name; String item; List<String> Al = new ArrayList<>(); Iterator<String> itr = testlist.iterator(); while (itr.hasNext()) { item = itr.next(); Al.add(item); } this.testlist = Al; } public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Java"); list.add("Oracle"); list.add("C++"); DeepAndShollowCopy copy=new DeepAndShollowCopy(10,"Testing", list); System.out.println(copy.toString()); } @Override public String toString() { return "DeepAndShollowCopy [id=" + id + ", name=" + name + ", testlist=" + testlist + "]"; } 

}

增加所有上面的定义,一个更多和最常用的深层副本,是在类的复制构造函数(或重载分配操作)。

浅拷贝 – >是当你不提供复制构造函数。 在这里,只有对象被复制,但并不是复制类的所有成员。

深拷贝 – 当你决定在你的类中实现拷贝构造函数或重载赋值,并且允许复制class级的所有成员。

 MyClass& MyClass(const MyClass& obj) // copy constructor for MyClass { // write your code, to copy all the members and return the new object } MyClass& operator=(const MyClass& obj) // overloading assignment operator, { // write your code, to copy all the members and return the new object } 

复制构造函数用于使用先前创build的同一个类的对象来初始化新对象。 默认情况下编译器写了一个浅拷贝。 浅拷贝工作正常,当dynamic内存分配不涉及,因为当涉及dynamic内存分配时,两个对象将指向一个堆中相同的内存位置。因此,为了消除这个问题,我们写了深拷贝,所以两个对象都有自己的属性副本在记忆中。 为了阅读完整的例子和解释的细节,你可以看到文章C ++构造函数 。

在浅拷贝之间添加一点混淆,并简单地给列表分配一个新的variables名称。

“说我们有:

 x = [ [1,2,3], [4,5,6], ] 

这个语句创build3个列表:2个内部列表和一个外部列表。 外部列表的引用然后在名称x下可用。 如果我们这样做

 y = x 

没有数据被复制。 我们在内存中仍然有相同的3个列表。 所有这些都是使外部列表在名称y下可用,除了以前的名称x。 如果我们这样做

 y = list(x) 

要么

 y = x[:] 

这将创build一个与x相同内容的新列表。 列表x包含对两个内部列表的引用,所以新列表还将包含对这两个内部列表的引用。 只有一个列表被复制 – 外部列表。 现在内存中有4个列表,两个内部列表,外部列表和外部列表的副本。 原始的外部列表在名称x下可用,并且新的外部列表在名称y下可用。

内部列表尚未被复制! 您可以在这里访问和编辑x或y的内部列表!

如果您有一个二维(或更高)列表或任何types的嵌套数据结构,并且您想要完整地复制所有内容,则您需要在复制模块中使用deepcopy()函数。 您的解决scheme也适用于二维列表,如遍历外部列表中的项目并复制每个项目的副本,然后为所有内部副本构build一个新的外部列表。

来源: https : //www.reddit.com/r/learnpython/comments/1afldr/why_is_copying_a_list_so_damn_difficult_in_python/