深拷贝与浅拷贝[重复]

Posted

技术标签:

【中文标题】深拷贝与浅拷贝[重复]【英文标题】:Deep copy vs Shallow Copy [duplicate] 【发布时间】:2011-02-09 02:40:39 【问题描述】:

可能重复:What is the difference between a deep copy and a shallow copy?

深拷贝和浅拷贝有什么区别。拷贝构造函数做了什么类型的拷贝?

【问题讨论】:

重复***.com/questions/184710/… 第一个问题是重复的,但我找不到第二个问题,尽管这可能是因为它没有明确说明。 @Ankur:在您的第二个问题中,您是在询问默认的复制构造函数吗? 深拷贝和浅拷贝这两个术语通常不会在 C++ 中使用,因为它们不能很好地映射到该语言。在 Java 和其他几种语言中,这种区别更有用,因为它们基于引用的语义,在大多数情况下,浅拷贝是不可避免的。在 C++ 中,对象是按值复制的,真正的浅拷贝非常罕见,但默认的复制构造函数也不会实现深拷贝。这些术语在 C++ 中没有意义 【参考方案1】:

浅拷贝:

副本的某些成员可能引用与原始对象相同的对象:

class X

private:
    int i;
    int *pi;
public:
    X()
        : pi(new int)
     
    X(const X& copy)   // <-- copy ctor
        : i(copy.i), pi(copy.pi)
     
;

这里,原始对象的pi 成员和复制的X 对象都将指向同一个int


深拷贝:

原始的所有成员都被克隆(如果需要,递归)。没有共享对象:

class X

private:
    int i;
    int *pi;
public:
    X()
        : pi(new int)
     
    X(const X& copy)   // <-- copy ctor
        : i(copy.i), pi(new int(*copy.pi))  // <-- note this line in particular!
     
;

这里,原始和复制的X 对象的pi 成员将指向不同的int 对象,但它们具有相同的值。


默认的拷贝构造函数(如果你自己不提供,它会自动提供)只创建浅拷贝。

更正: 下面的几个 cmets 已经正确指出,说默认的复制构造函数 always 执行浅拷贝(或深拷贝)是错误的事情)。一个类型的拷贝构造函数是创建浅拷贝还是深拷贝,或者介于两者之间的东西,取决于每个成员的拷贝行为的组合;毕竟,成员类型的复制构造函数可以做任何它想做的事情。

以下是 1998 年 C++ 标准的第 12.8 节第 8 段对上述代码示例的说明:

隐式定义的副本 类X 的构造函数执行一个 其子对象的成员副本。 [...] 每个子对象都复制到 适合其类型的方式:[...] [I]如果子对象是标量类型, 内置赋值运算符是 用过。

【讨论】:

神秘的段落似乎不同意您大胆声称默认复制构造函数仅生成浅拷贝。 是什么让你这么认为?我的理解如下:某些指针类型的成员是标量子对象,不是吗?如果你使用内置的赋值操作符复制一个指针,这意味着指向的对象不会被克隆,而只是被一个附加的指针引用。因此,您最终会得到一个浅拷贝。 @stakx 你是对的,但我认为这说明术语“深拷贝”和“浅拷贝”并不是特别有用——事实上,我从未听说有经验的 C++ 程序员使用它们。 默认的复制构造函数会生成一个member-wise的拷贝,而一个成员是深拷贝还是浅拷贝完全取决于成员的行为。 struct Person string firstName, lastName; - 默认复制构造函数进行 deep 复制。 所有可能的复制构造器“类型”的集合不包括 JUST “浅”和“深”的不相交集。它包括介于两者之间的所有内容(例如,一半的成员可能是浅拷贝,一半可能是深拷贝——但除非你知道他们的实现,否则你永远不会知道这些成员是在做浅拷贝还是深拷贝)。默认的复制构造函数既不总是“浅”也不总是“深”。浅层和深层这两个术语只是描述了可能的行为。【参考方案2】:

这方面的典型示例是指向结构或对象(可变)的指针数组。

浅拷贝复制数组并维护对原始对象的引用。

深拷贝也会复制(克隆)对象,因此它们与原始对象无关。这意味着对象本身被深度复制。这就是困难的地方,因为没有真正的方法可以知道某些内容是否被深度复制。

复制构造函数用于用之前创建的同一个类的对象来初始化新对象。默认情况下,编译器写了一个浅拷贝。当不涉及动态内存分配时,浅拷贝工作正常,因为当涉及动态内存分配时,两个对象将指向堆中的相同内存位置,因此为了解决这个问题,我们编写了深拷贝,因此两个对象都有自己的属性副本在记忆中。

要阅读包含完整示例和说明的详细信息,您可以查看文章Constructors and destructors。

默认的复制构造函数是浅的。您可以根据需要使自己的复制构造函数变深或变浅。见C++ Notes: OOP: Copy Constructors。

【讨论】:

它应该是一个指向可变对象的指针数组 一个很好的例子是 Boost::GIL,其中视图是图像对象(像素容器)的浅拷贝。当通过视图对象访问像素时,会生成像素的本地/临时副本。【参考方案3】:

深拷贝实际上是执行深拷贝。这意味着,如果你的类有一些引用字段,它们的值将被复制,而不是引用本身。例如,如果您有一个类的两个实例,A 和 B 具有引用类型的字段,并执行深层复制,则更改 A 中该字段的值不会影响 B 中的值。反之亦然。浅拷贝的情况不同,因为只复制引用,因此,在复制的对象中更改此字段会影响原始对象。

复制构造函数执行什么类型的副本?

它依赖于实现。这意味着对此没有严格的规定,您可以像深拷贝或浅拷贝一样实现它,但是据我所知,在复制构造函数中实现深拷贝是一种常见的做法。不过,默认的复制构造函数会执行浅拷贝。

【讨论】:

以上是关于深拷贝与浅拷贝[重复]的主要内容,如果未能解决你的问题,请参考以下文章

VUE开发 - 深拷贝与浅拷贝探讨

2.深拷贝与浅拷贝的区别

深拷贝与浅拷贝的实现(一)

JavaScript深拷贝与浅拷贝

深拷贝与浅拷贝详解

深拷贝与浅拷贝