【问题标题】:When does C++ perform deep copying and shallow copying?C++什么时候进行深拷贝和浅拷贝?
【发布时间】:2018-09-11 10:13:55
【问题描述】:

我想知道 C++ 什么时候做深拷贝,什么时候做浅拷贝。

例如:

int find()
{
    int n=5;
    return n;
}

为了在移出函数后删除n,它必须创建一个临时变量n,并将其返回给调用者。结果就是浅拷贝,对吗?

【问题讨论】:

  • 对于单个标量值,你觉得浅拷贝和深拷贝有什么区别?它怎么会变得更深?还要注意,当涉及到函数本地时,“删除”,普通的旧数据类型不是一个非常有用的概念;通常什么都不会“删除”,它只是在堆栈展开时留在周围,并且内存可能会在以后的一些函数调用中重用,而没有以任何有意义的方式清理。
  • “浅问题”发生在指针上;该地址被复制,但该地址处的内存内容未被复制。你必须手动复制它!
  • 每个副本都是“浅层的”,除非你做一些额外的事情,也就是说只有值被复制,而不是任何可能与之相关的东西。例如,将指针复制到数组的第一个元素不会复制数组。对于您的int,浅拷贝和深拷贝绝对没有区别
  • 我从未听说过有一种语言可以自己进行深度复制。
  • @molbdnilo 我真的很想用一个反例来震撼你,让你眼花缭乱,但我被难住了!

标签: c++


【解决方案1】:

结果是浅拷贝,对吗?

浅拷贝一直引用原始对象所引用的任何内容。深拷贝是指所引用资源的副本††(该资源的新副本必须由复制构造函数创建)。仅当复制的对象 是引用的,即它引用某个资源时,这种区别才有意义。

int 类型不引用任何对象,因此就类型系统而言,它不是引用的。然而,在类型系统之外,人们可以赋予它参考意义。例如,一个非常典型的整数表示资源的身份,例如存储在数据库中的实体。您需要考虑您的整数是否属于这种情况。在面向对象的设计中,此类标识符通常被包装到一个类††† 中(可以指定它以支持深拷贝)。

非类类型的副本总是浅的。只有复制构造函数†††† 可以执行深度复制。如果 5 标识了某个资源,那么 5 的副本也指的是同一资源。

深拷贝和浅拷贝相关的引用类型示例:引用、指针、具有引用成员的类。 usch 类的示例:智能指针、引用标识符的包装器†††。其中,指针和引用被浅拷贝,因为它们不是类。类实例的副本可以是浅的也可以是深的,这取决于复制构造函数的实现。

然后是移动构造。移动构造是一个浅拷贝,它修改原始对象以强制执行任何会被普通浅拷贝违反的类不变量。例如,唯一指针的移动构造函数会浅拷贝内部指针,并将原始指针设置为空,从而保持指针所有权的唯一性。


为了简单起见,我说对象,但这也适用于引用类型的副本——它们也不是对象。

†† 资源可能是内存中的另一个对象,或者例如文件描述符或执行线程。

††† 示例:std::thread 包装了操作系统提供的较低级别的标识符。

†††† 任何函数都可以执行深拷贝,但拷贝构造函数是唯一被拷贝初始化调用的函数。

【讨论】:

    【解决方案2】:

    术语“浅拷贝”和“深拷贝”通常被理解为与本身间接封装某些对象的类型有关。

    例如,一个带有指针的类(我们假设它指向某个东西):

    struct Foo
    {
       Bar* ptr;
    };
    

    当您复制Foo 时,Bar 是否也指向复制()?还是新的Foo 只是共享指向原始Bar 的原始指针()?

    这取决于复制的执行方式——通常您的Foo 将有一个复制构造函数,而正是这个复制构造函数中的代码做出了区分。

    例如,所有标准 C++ 容器(例如 vector)在内部都是由一组指向某些已分配缓冲区的指针组成的,但它们具有确保在复制向量时复制整个缓冲区的复制构造函数,因此每个向量都有其自己的独立缓冲区。这是一个深拷贝。

    但是我上面给出的示例,没有任何复制构造函数或其他可爱的代码,在分配时只会执行浅复制,因为我没有告诉它做任何事情,除了复制 ptr 的值。

    至于您的情况:int 只是一个值,因此我们无法进行此类比较。你只有一个价值副本,简单明了。实现该功能需要多少(如果有的话)临时的内部细节是不相关的(也不重要的);在这种情况下谈论浅拷贝和深拷贝是没有意义的,因为实际上没有任何封装的、间接持有的对象拥有或不拥有可以被视为浅拷贝或深拷贝或任何东西复制。对于任何非类类型,我们通常可以这么说。

    【讨论】:

      【解决方案3】:

      int 不是引用类型,因此不存在浅拷贝或深拷贝的问题。它只是一个副本。

      我们可以这样理解深拷贝和浅拷贝:

      深拷贝

      深拷贝复制所有字段,并复制字段指向的动态分配内存。当一个对象连同它所引用的对象一起被复制时,就会发生深拷贝。

      浅拷贝

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

      【讨论】:

      • 基本正确,尽管说浅拷贝是“对象的按位拷贝”在 C++ 中并不真正(至少不一定)正确。您仍然可以拥有高级复制语义,而无需深度复制所有成员。事实上,除非你使用 memcpy(或 std::copy 代替别名 char[]),否则情况几乎总是如此。
      【解决方案4】:

      您对代码行为的描述与您提供的源代码一致,但编译器可能不会这样做。

      C++ 编译器可以实现 as-if 规则:即您的源代码指定您的意图,而不是最终可执行文件的形式。

      换句话说,编译器可以用5 替换整个函数调用。

      简而言之,deep 副本会在源对象中与该对象相关的每个字节创建副本。任何小于该值的副本都是 shallow 副本。除非您以某种方式重载 =,否则 = 会采用浅拷贝。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-12
        • 2011-03-22
        • 1970-01-01
        • 2015-01-13
        • 1970-01-01
        • 2010-09-16
        • 2012-04-13
        • 1970-01-01
        相关资源
        最近更新 更多