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

时间:2008-10-08 20:22:43

标签: language-agnostic copy deep-copy shallow-copy

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

31 个答案:

答案 0 :(得分:772)

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

浅:

Before Copy Shallow Copying Shallow Done

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

深:

Before Copy Deep Copying Deep Done

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

答案 1 :(得分:665)

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

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

答案 2 :(得分:144)

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

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

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

答案 3 :(得分:68)

特别是对于iOS开发者:

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

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

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

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

B内存地址与A的

相同

B与A的

具有相同的内容

答案 4 :(得分:67)

尝试考虑以下图片

enter image description here

例如 Object.MemberwiseClone 会创建副本link

并使用 ICloneable 界面,您可以按照here

所述深度进行复制

答案 5 :(得分:59)

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

深层复制:将成员值从一个对象复制到另一个对象 任何指针对象都是重复的并且是深度复制的。

示例:

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

答案 6 :(得分:46)

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

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

使用深层复制时,将复制源指向的任何对象,并且目标指向该副本(因此现在每个引用的对象将有2个)。这会递归对象树。

答案 7 :(得分:37)

为了便于理解,您可以遵循以下文章: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm


浅拷贝:

Shallow Copy


深层复制:

Deep Copy

答案 8 :(得分:35)

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

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

深层复制 只需复制A的所有成员,在B的不同位置分配内存,然后将复制的成员分配给B以实现深层复制。这样,如果A变为不存在,则B在存储器中仍然有效。正确使用的术语是克隆,您知道它们两者完全相同但却不同(即存储为存储空间中的两个不同实体)。您还可以提供克隆包装器,您可以通过包含/排除列表决定在深层复制期间选择哪些属性。这是创建API时非常常见的做法。

您可以选择执行浅拷贝 ONLY_IF ,了解所涉及的赌注。如果你有大量的指针需要在C ++或C中处理,那么对象的浅层副本真的是一个坏主意。

EXAMPLE_OF_DEEP COPY _ 例如,当您尝试进行图像处理和对象识别时,需要在处理区域之外屏蔽“不相关和重复运动”。如果您正在使用图像指针,那么您可能具有保存这些蒙版图像的规范。现在......如果您执行图像的浅层复制,当指针引用从堆栈中被杀死时,您丢失了引用及其副本,即在某些时候存在访问冲突的运行时错误。在这种情况下,您需要的是克隆它的图像的深层副本。通过这种方式,您可以在将来需要时检索蒙版。

EXAMPLE_OF_SHALLOW_COPY 与StackOverflow中的用户相比,我不是很了解,所以请随意删除此部分并提供一个很好的示例,如果您可以澄清一下。但是我真的认为如果你知道你的程序将会运行一段无限的时间来进行浅层复制并不是一个好主意,即通过函数调用在堆栈上进行连续的“push-pop”操作。如果你向业余或新手演示某些东西(例如C / C ++教程),那么它可能没问题。但是如果你正在运行一个应用程序,如监视和检测系统,或声纳跟踪系统,你不应该对你的物体进行浅层复制,因为它迟早会杀死你的程序。

答案 9 :(得分:31)

char * Source = "Hello, world.";

char * ShallowCopy = Source;    

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

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

答案 10 :(得分:20)

什么是浅拷贝?

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

在此图中,MainObject1的字段field1的类型为int,ContainObject1的类型为ContainObject。当您执行MainObject1的浅表副本时,MainObject2创建时field2包含复制的field1值且仍然指向ContainObject1本身。请注意,由于field1是基本类型,因此其值将复制到field2,但由于ContainedObject1是对象,MainObject2仍指向ContainObject1。因此,ContainObject1中对MainObject1所做的任何更改都会反映在MainObject2中。

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

什么是深层复制?

深拷贝会复制所有字段,并复制字段指向的动态分配的内存。将对象与其引用的对象一起复制时,会发生深层复制。 Deep Copy

在此图中,MainObject1的字段field1的类型为int,ContainObject1的类型为ContainObject。当您执行MainObject1的深层复制时,MainObject2创建时field2包含复制的值field1ContainObject2,其中包含复制的值{{1} }}。请注意,对ContainObject1中的ContainObject1所做的任何更改都不会反映在MainObject1中。

good article

答案 11 :(得分:15)

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

在浅层副本中,将创建该类型的新实例,并将值复制到新实例中。参考指针也像值一样被复制。因此,引用指向原始对象。对引用存储的成员所做的任何更改都会同时显示在原始对象和副本中,因为没有对引用的对象进行复制。

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

答案 12 :(得分:12)

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

答案 13 :(得分:8)

浅克隆:
定义:&#34;对象的浅表副本复制'main'对象,但不复制内部对象。&#34; 当自定义对象(例如Employee)只有原始的String类型变量时,则使用Shallow Cloning。

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

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

深度克隆
定义:&#34;与浅拷贝不同,深拷贝是对象的完全独立的副本。&#34;
表示Employee对象拥有另一个自定义对象时:

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

然后你必须编写代码来克隆&#39;地址&#39;对象以及重写的clone()方法。否则,Address对象不会克隆,当您更改克隆的Employee对象中的Address值时会导致错误,这也反映了原始对象。

答案 14 :(得分:8)

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

答案 15 :(得分:7)

我想举例而非正式定义。

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 !!

答案 16 :(得分:7)

深层复制

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

浅拷贝

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

答案 17 :(得分:7)

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

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

  

clone总是做浅拷贝。

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

} 

上面的OutPut将是 -

  

false true true

     

false false false

对原始对象所做的任何更改都将反映在浅层对象中而不是深层对象中。

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

OutPut - ViSuaLBaSiC C

答案 18 :(得分:5)

简单来说,浅拷贝类似于按引用呼叫,深拷贝类似于按值呼叫

在Call By Reference中,函数的形式参数和实际参数都指的是相同的内存位置和值。

在Call By Value中,函数的形式参数和实际参数都指的是不同的内存位置,但具有相同的值。

答案 19 :(得分:5)

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

答案 20 :(得分:5)

想象一下,有两个名为arr1和arr2的数组。

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

答案 21 :(得分:2)

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

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

答案 22 :(得分:2)

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

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

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

<强>解释

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

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

答案 23 :(得分:2)

要向其他答案添加更多内容,

  • 对象的浅副本为值类型按值执行复制 基于属性的基础属性,并通过引用进行复制。
  • 对象的深层副本基于值类型执行值复制 属性,以及基于引用类型的值复制 层次结构深层的属性(引用类型)

答案 24 :(得分:2)

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

与浅层复制不同,deepcopy构造新的复合对象,并且还插入原始复合对象的原始对象的副本。

让我们举个例子。

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

上面的代码打印为FALSE。

让我们看看如何。

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

enter image description here

如图所示,列表中有一个列表。

然后我们使用y = copy.copy(x)创建它的浅表副本。 python在这里做的是,它将创建一个新的复合对象,但它们内部的对象指向orignal对象。

enter image description here

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

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

enter image description here

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

HTH。

答案 25 :(得分:2)

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

这是解释深层和浅层副本的程序。

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

答案 26 :(得分:1)

复制ararys:

Array是一个类,这意味着它是引用类型,因此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]);
    }

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

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

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

要创建深层副本 - 其中引用类型是重复的,您必须遍历该数组并手动克隆每个元素。

答案 27 :(得分:0)

我从以下几行中了解了。

浅表复制将对象的值类型(int,float,bool)字段复制到目标对象,并将对象的引用类型(字符串,类等)复制为引用在目标对象中。在该目标中,引用类型将指向源对象的内存位置。

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

答案 28 :(得分:0)

为浅层复制之间的混淆添加一点,并简单地指定一个新的变量名列表。

“说我们有:

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

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

y = x

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

y = list(x)

y = x[:]

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

提供

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

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

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

答案 29 :(得分:0)

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

答案 30 :(得分:0)

添加到上述所有定义中,还有一个也是最常用的深层副本,位于类的复制构造函数(或重载赋值操作符)中。

浅拷贝 - &gt;是你不提供复制构造函数。在这里,只复制对象,但不会复制该类的所有成员。

深层复制 - &gt;是你决定在你的类中实现复制构造函数或重载赋值,并允许复制类的所有成员。

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
}