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

753

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

31个回答

37
char * Source = "Hello, world.";

char * ShallowCopy = Source;    

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

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


17

深拷贝

深拷贝会拷贝所有字段,并复制指向这些字段的动态分配的内存。当一个对象被复制时,如果它引用了其他对象,则也会将其引用的对象一起复制。

浅拷贝

浅拷贝是对一个对象进行逐位拷贝的操作。创建一个新对象,在原始对象中的所有值都得到了精确拷贝。如果对象中的任何字段都是对其他对象的引用,则只会拷贝引用地址,即仅拷贝内存地址。


那个链接很不幸已经失效了,现在它指向的是一篇有关于网络设计的 2019 年 2 月文章(除非作者有先见之明?)。 - PhilPhil

17
在面向对象编程中,类型包括一组成员字段。这些字段可以按值或引用(即指向某个值的指针)存储。
在浅复制中,创建类型的新实例并将值复制到新实例中。引用指针也像值一样被复制。因此,引用指向原始对象。由于未对引用对象进行副本,因此对存储引用的成员进行的任何更改都会同时显示在原始对象和复制品中。
在深复制中,按值存储的字段按照以前的方式进行复制,但是不复制对引用对象的指针。相反,所引用对象会产生一个深层次的复制,并存储指向新对象的指针。对这些所引用的对象所做的任何更改都不会影响对象的其他副本。

15

我想举一个例子而不是给出正式定义。

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

我得到了 1 1 4 4 4 4 4 4 - Suresh Prajapati
在深拷贝中,执行copyObject.a = 8,然后检查结果。希望你能得到正确的答案。 - Vivek Mehta
object.assign({},arr) 不会创建深拷贝,假设我们有以下对象 var source = {"foo":1,"name":"Testing",c:{age:34}} var dCopy = Object.assign({},source)console.log(dCopy.c.age) console.log(Source deep ${source.c.age})source.c.age = 3console.log(dCopy.c.age) console.log(Source deep ${source.c.age}) - Swarup Chavan

13

'ShallowCopy'指向和'Source'相同的内存位置。'DeepCopy'指向不同的内存位置,但内容相同。


1
这有点误导人。浅拷贝和深拷贝都会将对象复制到内存中的新位置,深拷贝还会复制子对象,而浅拷贝只是让新对象引用旧的子对象。如果不参考原始对象,很难阅读。 - Bill K

12

浅拷贝:
定义:“对象的浅拷贝复制了‘主’对象,但不会复制内部对象。”当自定义对象(例如:Employee)只拥有原始类型和字符串类型变量时,可以使用浅拷贝。

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

在重写的clone()方法中返回super.clone();,你的工作就完成了。

深度克隆
定义:“与浅拷贝不同,深拷贝是一个完全独立的对象副本。”
这意味着当一个Employee对象持有另一个自定义对象时:

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

接下来,您需要编写代码在重写的clone()方法中克隆'Address'对象。否则,地址对象将不会被克隆,并且当您更改克隆的Employee对象中的地址值时,它会影响原始对象。


10

浅拷贝- 原始对象和浅拷贝对象中的引用变量都指向同一个对象。

深拷贝- 原始对象和深拷贝对象中的引用变量指向不同的对象。

克隆操作总是进行浅拷贝。

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

输出 - Visual Basic C


10
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

这不是一个好的例子。浅拷贝主要用于快速复制对象,而不复制数据,但一旦对象需要修改共享数据,就会对其进行深拷贝。你的例子可能会让初学者感到困惑。 - CMircea
这仅适用于使用指针表示字符串的语言。DHA试图表达的观点是,浅拷贝仅复制到相同(单数)原始内容的指针,而深拷贝还会克隆指针所引用的内容。两种方法都复制表面内容。如果语言将字符串存储为表面文字内容,例如在WAV标头中,则此示例将无法工作。请注意,这对于大多数不是奇特的实际问题来说可能过于挑剔。 - DragonLord

8
假设有两个数组,分别称为arr1和arr2。
arr1 = arr2;   //shallow copy
arr1 = arr2.clone(); //deep copy

7

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

与浅拷贝不同,深拷贝会构建新的复合对象,并且还会插入原始复合对象中原始对象的副本。

让我们来看一个例子。

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 在此处所做的是创建一个新的复合对象,但其中的对象指向原始对象。

enter image description here

在图像中,它为外部列表创建了一个新副本。但是内部列表仍然与原始列表相同。
现在我们使用z = copy.deepcopy(x)创建它的深层副本。Python在这里所做的是,它将为外部列表和内部列表创建新对象。如下图所示(红色突出显示)。

enter image description here

代码结束时打印 False,因为 y 和 z 不是同一个对象。

希望对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接