深抄和浅抄有什么区别

深抄和浅抄有什么区别

779509 次浏览

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

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

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

浅复制:将成员值从一个对象复制到另一个对象。

Deep Copy:将成员值从一个对象复制到另一个对象。
任何指针对象都被复制和深度复制。

示例:

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.)

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

深度副本复制所有内容。集合的深度副本是两个集合,其中复制了原始集合中的所有元素。

char * Source = "Hello, world.";


char * ShallowCopy = Source;


char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);

'ShlowCopy'指向内存中与'Source'相同的位置。 “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

我还没有看到一个简短的,容易理解的答案,所以我会试一试。

使用浅拷贝,源指向的任何对象也会被目标指向(因此不会复制引用的对象)。

使用深度副本,源指向的任何对象都会被复制,副本也会被目标指向(因此现在每个引用的对象都有2个)。这在对象树中递归。

广度vs深度;考虑以对象作为根节点的引用树。

浅:

复制前浅复制浅复制

变量A和B指的是内存的不同区域,当B被分配给A时,两个变量指的是同一个内存区域。后来对其中一个内容的修改会立即反映在另一个的内容中,因为它们共享内容。

深度:

复制前深度复制深度复制

变量A和B指的是内存的不同区域,当B被分配给A时,A指向的内存区域中的值被复制到B指向的内存区域中。后来对A或B的内容的修改保持唯一;内容不共享。

在面向对象程序设计中,类型包括成员字段的集合。这些字段可以通过值或引用(即指向值的指针)存储。

在浅拷贝中,创建该类型的新实例,并将值复制到新实例中。引用指针也像值一样被复制。因此,引用指向原始对象。对通过引用存储的成员的任何更改都会出现在原始和副本中,因为没有对引用对象进行复制。

在深度复制中,按值存储的字段会像以前一样复制,但不复制指向通过引用存储的对象的指针。相反,对引用的对象进行深度复制,并存储指向新对象的指针。对这些引用对象所做的任何更改都不会影响该对象的其他副本。

“浅拷贝”指向内存中与“源”相同的位置。“深度拷贝”指向内存中不同的位置,但内容是相同的。

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

在简单术语中,浅拷贝类似于按引用调用,深拷贝类似于按值调用

在引用调用中,函数的形式参数和实际参数都引用相同的内存位置和值。

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

复制ararys:

数组是一个类,这意味着它是引用类型,因此array1=array2结果 在引用相同数组的两个变量中。

但是看看这个例子:

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

浅克隆表示只复制克隆数组表示的内存。

如果数组包含值类型对象,则复制值

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

要创建复制引用类型的深度副本,您必须循环遍历数组并手动克隆每个元素。

特别是iOS开发者:

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

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

如果BA深度复制,那么它就像B = [A copy];

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

B的内存地址与A的内存地址相同

B和A的含量相同

{想象两个对象:A和B的类型相同_t(相对于C++),你正在考虑浅/深复制A到B}

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

深度拷贝: 简单地复制A的所有成员,在不同的位置为B分配内存,然后将复制的成员分配给B以实现深度复制。通过这种方式,如果A变得不存在,B在内存中仍然有效。正确的术语是克隆,在这里你知道它们都是完全相同的,但又不同(即作为两个不同的实体存储在内存空间中)。你还可以提供克隆包装器,在其中你可以通过包含/排除列表决定在深度复制期间选择哪些属性。这是创建API时很常见的做法。

你可以选择做一个浅拷贝ONLY_IF,你知道其中的利害关系。当你在C++或C中有大量的指针要处理时,做一个对象的浅拷贝是一个坏主意。

EXAMPLE_OF_DEEPCOPY_一个例子是,当你试图做图像处理和对象识别时,你需要在处理区域外屏蔽“无关和重复的运动”。如果你使用的是图像指针,那么你可能有保存这些掩码图像的规范。现在…如果你做图像的浅副本,当指针引用从堆栈中被KILLED时,你失去了引用及其副本,即在某些时候会有访问冲突的运行时错误。在这种情况下,你需要的是通过克隆它来复制图像的深度副本。通过这种方式,你可以检索掩码,以防将来需要它们。

EXAMPLE_OF_SHALLOW_COPY与StackOverflow中的用户相比,我并不是非常了解,所以如果你能澄清的话,请随时删除这部分并放一个好的例子。但我真的认为,如果你知道你的程序将运行无限长的时间,即通过函数调用在堆栈上连续的“push-pop”操作,做浅拷贝不是一个好主意。如果你是在向业余或新手演示一些东西(例如C/C++教程的东西)那么它可能是好的。但是,如果您正在运行监视和检测系统或声纳跟踪系统等应用程序,则不应该继续浅薄地复制对象,因为它迟早会杀死您的程序。

复制是创建一个新对象,然后将当前对象的非静态字段复制到新对象。如果字段是值类型-->执行字段的逐位复制;对于引用类型-->复制引用,但引用的对象没有;因此原始对象及其克隆引用相同的对象。

复制是创建一个新对象,然后将当前对象的非静态字段复制到新对象。如果字段是值类型-->,则执行该字段的逐位复制。如果字段是引用类型-->,则执行引用对象的新副本。要克隆的类必须标记为[Serializable]。

除了上述所有定义之外,还有一个最常用的深度复制是在类的复制构造函数(或重载赋值操作器)中。

浅拷贝-->是指您不提供拷贝构造函数。在这里,只复制对象,而不是复制类的所有成员。

深度复制-->是指您决定在类中实现复制构造函数或重载赋值,并允许复制类的所有成员。

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
}

复制构造函数用于使用之前创建的同一类的对象初始化新对象。默认情况下编译器编写浅拷贝。当不涉及动态存储分配时,浅拷贝工作正常,因为当涉及动态存储分配时,两个对象将指向堆中相同的内存位置,因此为了解决这个问题,我们编写了深度拷贝,这样两个对象在内存中都有自己的属性副本。 为了阅读完整示例和解释的详细信息,您可以查看文章C++构造函数

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

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

EYZ0将一个对象的内容复制到同一个类的另一个实例中,从而形成镜像。由于直接复制引用和指针,这两个对象将共享另一个对象相同的外部包含内容,这是不可预测的。

说明:

使用复制构造函数,我们只需逐个成员复制数据值。这种复制方法称为浅复制。如果对象是一个简单的类,由内置类型组成,没有指针,这是可以接受的。此函数将使用值和对象,其行为不会通过浅复制改变,仅复制成员指针的地址,而不是地址指向的值。对象的数据值将被函数无意中更改。当函数超出范围时,对象的副本及其所有数据将从堆栈中弹出。

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

为了便于理解,你可以关注这篇文章: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm


浅拷贝:

浅拷贝


深度复制:

深度复制

深度复制

深度复制复制所有字段,并复制字段指向的动态分配内存。当对象与其引用的对象一起复制时,就会发生深度复制。

浅拷贝

浅拷贝是对象的逐位拷贝。创建一个新对象,该对象具有原始对象中值的精确副本。如果对象的任何字段是对其他对象的引用,则仅复制引用地址,即仅复制内存地址。

什么是浅拷贝?

浅拷贝是对象的逐位复制。创建一个新对象,该对象具有原始对象中值的精确副本。如果对象的任何字段是对其他对象的引用,则仅复制引用地址,即仅复制内存地址。浅拷贝

在此图中,MainObject1具有int类型的字段field1ContainObject类型的字段ContainObject1。当您对MainObject1进行浅层复制时,MainObject2是使用field2创建的,其中包含复制的field1的值,并且仍然指向ContainObject1本身。请注意,由于field1是原始类型,因此它的值被复制到field2,但由于field11是一个对象,因此MainObject2仍然指向ContainObject1。因此在MainObject1中对ContainObject1所做的任何更改都将反映在MainObject2中。

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

什么是深度复制?

深度复制复制所有字段,并复制字段指向的动态分配内存。当对象与其引用的对象一起复制时,会发生深度复制。 深度复制

在此图中,MainObject1具有int类型的字段field1ContainObject类型的字段ContainObject1。当您对MainObject1进行深度复制时,将创建MainObject2,其中field2包含复制的field1值,ContainObject2包含复制的ContainObject1值。请注意,在MainObject1中对ContainObject1所做的任何更改都不会反映在MainObject2中。

好文章

浅拷贝-原始和浅复制对象中的引用变量引用常见对象。

深度复制-原始和深度复制对象中的引用变量引用不同对象。

克隆总是做浅复制。

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
      

}

上述输出将为─

false true true

false false false

原始对象的任何变化都会反映在浅层对象而不是深层对象中。

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

输出- ViSuaLBaSiC C语言环境

假设有两个数组称为arr1和arr2。

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

尝试考虑以下图像

在此处输入图片描述

例如对象。成员明智的克隆创建一个副本链接

使用冰封的接口,您可以获得副本,如所述这里

为了给其他答案添加更多内容,

  • 对象的浅拷贝对值类型执行按值复制 的属性,并按引用复制基于引用类型的属性。
  • 对象的Deep Copy根据值类型执行按值复制 属性,以及基于引用类型的按值复制 层次结构深处的属性(引用类型)

为了在浅拷贝和简单地为列表分配一个新的变量名之间添加更多的混淆。

“说我们有:

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

此语句创建3个列表:2个内部列表和一个外部列表。然后在名称x下提供对外部列表的引用。如果我们这样做

y = x

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

y = list(x)

y = x[:]

这将创建一个与x内容相同的新列表。List x包含对2个内部列表的引用,因此新列表也将包含对相同的2个内部列表的引用。只复制一个列表-外部列表。 现在内存中有4个列表,两个内部列表,外部列表和外部列表的副本。原始外部列表在名称x下可用,新的外部列表在名称y下可用。

内部列表尚未复制!此时您可以从x或y访问和编辑内部列表!

如果你有一个二维(或更高)列表,或任何类型的嵌套数据结构,并且你想制作所有内容的完整副本,那么你想在复制模块中使用深度复制()函数。你的解决方案也适用于二维列表,因为迭代外部列表中的项目并对每个项目进行复制,然后为所有内部副本构建一个新的外部列表。

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

浅拷贝不会创建新引用,但深拷贝会创建新引用。

下面是解释深拷贝和浅拷贝的程序。

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 + "]";
}
}

浅层克隆:
定义:“对象的浅拷贝复制'main'对象,但不复制内部对象。 当自定义对象(例如员工)只有原始的String类型变量时,则使用浅克隆。

Employee e = new Employee(2, "john cena");
Employee e2=e.clone();

您在重写的clone()方法中返回super.clone();,您的工作就结束了。

深度克隆
定义:“与浅拷贝不同,深拷贝是对象的完全独立拷贝。”
表示当雇员对象持有另一个自定义对象时:

Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");

然后,您必须编写代码以在重写的clone()方法中克隆“地址”对象。否则,地址对象将无法克隆,并且当您更改克隆的员工对象中的地址值时会导致bug,这也反映了原始对象。

浅拷贝构造一个新的复合对象并将其引用插入到原始对象中。

与浅拷贝不同,深拷贝构造新的复合对象并插入原始复合对象的原始对象的副本。

让我们举个例子。

import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)

上面的代码打印FALSE。

让我们看看如何。

原始复合对象x=[1,[2]](称为复合对象,因为它在对象内部有对象(盗梦空间))

在此处输入图片描述

正如您在图片中看到的,列表中有一个列表。

然后我们使用y = copy.copy(x)创建它的浅层副本。python在这里所做的是,它将创建一个新的复合对象,但其中的对象指向原始对象。

在此处输入图片描述

在图像中,它为外部列表创建了一个新副本。但内部列表与原始列表相同。

现在我们使用z = copy.deepcopy(x)创建它的深度副本。python在这里所做的是,它将为外部列表和内部列表创建新对象。如下图所示(红色突出显示)。

在此处输入图片描述

最后代码打印False,因为y和z不是同一个对象。

嗯。

我想举个例子,而不是正式的定义。

var originalObject = {
a : 1,
b : 2,
c : 3,
};

这段代码显示了一个浅拷贝

var copyObject1 = originalObject;


console.log(copyObject1.a);         // it will print 1
console.log(originalObject.a);       // it will also print 1
copyObject1.a = 4;
console.log(copyObject1.a);           //now it will print 4
console.log(originalObject.a);       // now it will also print 4


var copyObject2 = Object.assign({}, originalObject);


console.log(copyObject2.a);        // it will print 1
console.log(originalObject.a);      // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a);        // now it will print 4
console.log(originalObject.a);      // now it will print 1

这段代码显示了一个深度复制

var copyObject2 = Object.assign({}, originalObject);


console.log(copyObject2.a);        // it will print 1
console.log(originalObject.a);      // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a);        // now it will print 4
console.log(originalObject.a);      // !! now it will print 1 !!

我是从以下几行字中明白过来的。

浅拷贝将对象值类型(int、浮点数、bool)字段复制到目标对象,对象的引用类型(字符串、类等)在目标对象中复制为参考文献。在这个目标引用类型将指向源对象的内存位置。

深度复制将对象的值和引用类型复制到目标对象的完整新副本中。这意味着值类型和引用类型都将被分配一个新的内存位置。