Language agnostic 复制构造函数/运算符/函数是否需要明确它实现的复制变量?

Language agnostic 复制构造函数/运算符/函数是否需要明确它实现的复制变量?,language-agnostic,copy,deep-copy,shallow-copy,Language Agnostic,Copy,Deep Copy,Shallow Copy,昨天我在C#中问了这个问题,大多数答案都集中在深度复制和浅层复制之间的区别,以及应该明确给定的复制构造函数(或运算符或函数)实现了哪两种复制变体。我觉得这很奇怪 我在C++中写了很多软件,一个很大程度上依赖于复制的语言,我从来没有需要过多个拷贝变体。我使用过的唯一一种复制操作是我称之为“足够深的复制”的操作。它做了以下工作: 如果对象拥有成员变量(cf)的所有权,则递归复制该对象 如果对象对成员变量(cf)没有所有权,则只复制链接 现在,我的问题有三个: 1) 一个对象是否需要多个副本变量

昨天我在C#中问了这个问题,大多数答案都集中在深度复制和浅层复制之间的区别,以及应该明确给定的复制构造函数(或运算符或函数)实现了哪两种复制变体。我觉得这很奇怪

我在C++中写了很多软件,一个很大程度上依赖于复制的语言,我从来没有需要过多个拷贝变体。我使用过的唯一一种复制操作是我称之为“足够深的复制”的操作。它做了以下工作:

  • 如果对象拥有成员变量(cf)的所有权,则递归复制该对象
  • 如果对象对成员变量(cf)没有所有权,则只复制链接
现在,我的问题有三个:

  • 1) 一个对象是否需要多个副本变量
  • 2) 复制功能是否需要明确它实现的复制变体
  • 3) 顺便问一下,有没有更好的术语来形容我所说的“足够深入的复制”?我问了一个问题

    • 一个对象只需要复制它需要复制的内容。虽然这个问题被标记为语言不可知论,你提到C++,我更愿意用C术语解释(因为这是我最熟悉的)。然而,这些概念是相似的

      值类型类似于结构。它们直接存在于对象实例中。因此,复制对象时,除了复制值类型之外别无选择。所以,你通常不必担心这些

      引用类型就像指针,这就是它变得棘手的地方。根据引用类型的不同,您可能需要也可能不需要深度副本。一般的经验法则是,如果引用类型(作为对象的成员)取决于外部对象的状态,则应将其克隆。如果不是,也永远不会,那就不必了

      另一种思考方式是,从外部传入对象的对象可能不应该被克隆。类生成的对象应为

      好吧,我撒谎了,我会用一些C++,因为它能最好地解释我的意思。
      class MyClass {
          int foo;
          char * bar;
          char * baz;
      
      public: MyClass(int f, char * str) {
              this->foo = f;
              bar = new char[f];
              this->baz = str;
          }
      };
      
      对于这个对象,需要处理两个字符串缓冲区。第一个是由类本身创建和管理的
      bar
      。克隆对象时,应分配新的缓冲区

      另一方面,baz,不应该是。事实上,你不能,因为你没有足够的信息来这么做。应该只复制指针

      当然,
      foo
      只是一个数字。复制一下,没什么好担心的:)

      总之,要直接回答您的问题:

    • 99%的情况下,没有。只有一种复制方法是有意义的。然而,这种方式是不同的
    • 不直接。记录它是一个好主意,但是任何内部的东西都应该是内部的
    • 只是“深度复制”。您应该(编辑:几乎)永远不要尝试克隆您不控制的对象或指针,因此这不受规则限制:)

    • <>大多数C++程序员不使用术语“浅拷贝”和“深层拷贝”,这是非常好的理由,通常只有一种方法复制对象。C++中尤其如此,因为编译器在很多情况下使用复制构造函数,程序员可以告诉它使用哪个复制构造函数,例如:

      void f( std::string s );
      

      没有办法告诉编译器如何复制字符串。

      作为实现细节,“深度复制”和“浅层复制”之间的区别是有意义的,但允许它泄漏到这一点之外通常表明存在缺陷的抽象,它可能会以其他方式表现出来。

      如果对象
      Foo
      持有对象引用纯粹是为了封装其中包含的对象的不可变方面(身份除外),则
      Foo
      的正确副本可能包含引用的副本或对封装对象副本的引用。

      如果一个对象
      Foo
      持有一个对象引用纯粹是为了封装对象的可变和不可变方面,而不是身份,但对该对象的引用永远不会暴露于任何会使其发生变异的东西,那么同样的情况也适用。

      如果对象
      Foo
      持有对象引用纯粹是为了封装对象的可变和不可变方面,而不是身份,并且所讨论的对象将发生变异,那么
      Foo
      的正确副本必须包含对封装对象副本的引用。

      如果对象
      Foo
      持有对象引用纯粹是为了封装对象的不可变方面,包括标识,则
      Foo
      的正确副本必须包含引用的副本;它不能包含对重复对象的引用。

      如果对象
      Foo
      持有一个对象引用以封装可变状态和对象标识,则不可能单独生成
      Foo
      的正确副本。
      Foo
      的正确副本只能通过复制其所连接的整个对象集来生成。


      谈论“浅拷贝”唯一有意义的时候是当一个不完整的操作被用作制作正确拷贝的步骤之一时。否则,只有一个正确的副本“深度”,由封装在对象引用中的状态类型控制。

      回答有点晚,但c++11或多或少地介绍了您:

      解决方案是,如中所述,使用不同的指针类型来表示您拥有的(共享)所有权类型

      由于
      std::unique_ptr
      不可复制,您将被迫复制unique指针所拥有的数据。从会员所有权的角度说明一切,可能总是能够清楚地说明使用哪种副本