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

753

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

31个回答

7
在简单的术语中,浅拷贝类似于按引用调用,而深拷贝类似于按值调用。
在按引用调用中,函数的形式参数和实际参数都指向同一内存位置和值。
在按值调用中,函数的形式参数和实际参数指向不同的内存位置,但具有相同的值。

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

4

浅复制不会创建新引用,但深复制会创建新引用。

下面是一个程序来解释深复制和浅复制。

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

4

除其他答案外,

  • 对象的浅复制对于基于值类型的属性执行按值复制,并对于基于引用类型的属性执行按引用复制。
  • 对象的深复制对于基于值类型的属性执行按值复制,对于层次结构中(引用类型的)基于引用类型的属性也执行按值复制。

3

我从以下内容中了解到:

浅复制将对象的值类型(int, float, bool)字段复制到目标对象中,而对象的引用类型(string, class等)则作为引用复制到目标对象中。在这种情况下,目标引用类型将指向源对象的内存位置。

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


3

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

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

浅复制将一个对象的内容复制到同一类的另一个实例中,从而创建镜像。由于直接复制引用和指针,两个对象将共享另一个对象的外部包含内容,因此具体情况难以预测。

解释:

使用复制构造函数只需成员逐个复制数据值即可。这种复制方法称为浅复制。如果对象是简单类,由内置类型组成且没有指针,那么这是可以接受的。该函数将使用对象的值和行为不会受到浅复制的影响,只是成员指针的地址被复制而不是指针指向的值。该对象的数据值将被无意中更改。当函数超出作用域时,具有其所有数据的对象的副本将从堆栈中弹出。

如果对象有任何指针,则需要执行深复制。对于对象的深度复制,会在自由存储器中为对象分配内存,并复制指向的元素。深复制用于从函数返回的对象。


2

浅复制是创建一个新对象,然后将当前对象的非静态字段复制到新对象中。如果字段是值类型,则执行位按位复制;对于引用类型,则复制引用但不复制所指对象;因此原始对象和其克隆体引用同一对象。

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


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

1

复制数组:

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

浅克隆意味着只复制克隆数组所表示的内存。

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

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

要创建深层副本 - 其中引用类型被复制,必须循环遍历数组并手动克隆每个元素。


在C#/VB中,我不知道其他语言的情况,但是浅复制值类型的数组不会复制其值。两个数组指向同一对象。添加一个按钮到窗体上,将以下代码添加进去即可查看:`private void button1_Click(object sender, EventArgs e) { int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{6,7,8,9,0}; MessageBox.Show(arr1[2] + " " + arr2[2]); arr2 = arr1; MessageBox.Show(arr1[2] + " " + arr2[2]); arr1[2] = 12; MessageBox.Show(arr1[2] + " " + arr2[2]); }` - DeanOC
你是对的,我纠正了我的答案,使用数组克隆更加精确。你绝对正确,"浅复制值类型数组不会复制值",但是使用数组克隆可以。我已经尝试解释过了,请自行尝试。谢谢。 - komizo

1

为了更加混淆浅拷贝和仅将新的变量名分配给列表之间的区别,我们只需再添加一点内容。

“假设我们有:

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

这个语句创建了3个列表:2个内部列表和一个外部列表。然后以变量名x的形式提供对外部列表的引用。如果我们执行

y = x

没有数据被复制。我们仍然在内存中有相同的3个列表。所有这些只是使外部列表在其先前名称x之外,也可用名称y访问。如果我们执行

y = list(x)

或者

y = x[:]

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

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

如果您有一个二维(或更高)列表或任何类型的嵌套数据结构,并且想要完全复制所有内容,则应使用copy模块中的deepcopy()函数。您的解决方案也适用于2-D列表,因为它遍历外部列表中的项目并复制每个项目,然后为所有内部副本构建一个新的外部列表。

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


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