【问题标题】:Is having a clone() method a 'Good Idea'?有一个 clone() 方法是一个“好主意”吗?
【发布时间】:2014-07-17 10:33:48
【问题描述】:

我记得在 Java 中看到过一些 clone() 用法,现在我在一些 C++ 代码中注意到了它。我也在 StackExchange 上注意到它们:

Copying a Polymorphic object in C++

那么,拥有克隆方法是一个“好主意”吗?还是复制构造函数就足够了?

【问题讨论】:

  • 只想要浅拷贝和深拷贝之间可能存在语义差异,在这种情况下,克隆将是对深拷贝所有内容的显式调用,因此它是不同的副本而不是“引用” ' 到共享对象,具体取决于您如何实现赋值运算符和复制构造函数
  • 带有垃圾收集器的语言对复制对象的需求要少得多。例如,它们可以安全地存储指向集合中对象的指针,而不必担心所有权和确定性清理。所以他们没有任何类似于copy ctor的东西。因此,如果程序员想要提供复制语义,那么他必须编写一个显式方法来生成副本。通常称为 clone()。
  • @einpoklum 不,复制构造函数被假定为用户代码的深层复制。
  • 我看到有些人不允许赋值和复制构造函数,因此你必须调用clone 来获取对象的副本,但这实际上取决于你想要定义和遵守的合同跨度>
  • 多态性的问题是你不知道在编译时被复制的确切类型,这是复制构造函数所需要的。因此,一个克隆方法,为了安全起见,可能带有禁用的复制构造函数。查找:对象切片。

标签: c++ clone copy-constructor idioms


【解决方案1】:

拥有克隆方法是一个“好主意”吗?还是应该复制 ctor 就足够了?

复制构造函数只能复制他们知道的数据成员 - 这不包括它们可能嵌入的任何派生对象中的数据成员。即如果class X 的复制构造函数用于通过X*X& 复制对象地址,但实际运行时数据对象是具有附加数据成员的派生类型,则会出现问题。

virtual clone 方法是一个合适的解决方案,让派生类指定用于复制该类型对象的代码。

如果您仍然无法清楚地看到好处,您应该阅读 O.O.一般来说,设计和多态性,那么对此的需求应该开始变得清晰。

【讨论】:

  • 但是,这是否意味着 clone() 对于非最终类是必要的? (即有子类的类)?
  • @einpoklum 不完全...您可能不需要复制它们,例如 - 如果它们是由工厂创建的,那么引用/指针会被传递,或者可能是你重新复制,但只能在编译时知道运行时类型的代码中(例如 Derived4 d; ...; Derived4 e(d); 甚至 if (Derived2* p = dynamic_cast<Derived2>(p_base)) { Derived2* p_another = new Derived2(*p); ... })。
  • 好吧,让我重新表述一下:对于具有子类并且被忽略子类的代码复制的类,clone() 不是必需的吗?
  • @einpoklum:几乎,virtual clone() - 和派生类实现 - 对于具有子类的类是必需的,其中这些子类被忽略子类的代码复制。
  • 总体上阅读 OOP 可以给您一个提示,但我会说阅读该特定部分,因为它是 OOP 中更高级的主题。
【解决方案2】:

克隆的原因是创建对象的精确副本。因此,在最坏的情况下,您将使用两倍的内存量。因为字符串经常被实习并且不会被克隆,因此您可能需要强制转换它。虽然由克隆方法/复制构造函数的实现者来实现它。

【讨论】:

  • 这是克隆与复制构造的单一普遍接受的语义吗?
  • 在 C++ 中,标准要求从另一个 std::string 复制构造或分配值的 std::string 将像深度复制一样运行 - 您的代码永远不需要“强制转换” "或做任何其他不寻常的事情。如果实现试图做一些聪明的“实习”,那么实现的问题就是如何确保你的代码仍然保持不变(这就是为什么我相信所有早期的 Copy-On-Write std::string 实现的尝试都被放弃了)。
  • 我不明白这个答案。当然,使用两倍的内存量是最好的情况,因为您正在创建副本?关于字符串的部分太模棱两可而无用。也许您指的是手动分配的数组。
猜你喜欢
  • 1970-01-01
  • 2011-02-09
  • 2012-10-16
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多