15

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

我用 C++ 编写了很多软件,这是一种严重依赖复制的语言,而且我从来不需要多个复制变体。我曾经使用过的唯一一种复制操作是我称之为“足够深的复制”的操作。它执行以下操作:

  • 如果对象拥有成员变量的所有权(参见composition),它会被递归复制。
  • 如果对象对成员变量没有所有权(参见聚合),则仅复制链接。

现在,我的问题有三个:

  • 1) 一个对象是否需要多个副本变体?
  • 2)复制功能是否需要明确它实现的复制变体?
  • 3)顺便说一句,我所说的“足够深的副本”是否有更好的术语?我问了一个有关“深拷贝”一词定义的相关问题
4

4 回答 4

5

一个对象只需要复制它需要复制的东西。尽管这个问题被标记为与语言无关,并且您提到了 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这只是一个数字。复制它,没有什么可担心的:)

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

  1. 99%的时候,没有。只有一种复制方法是有意义的。然而,这种方式各不相同。
  2. 不是直接的。记录它是一个好主意,但任何内部的东西都应该保留在内部。
  3. 只是“深拷贝”。您应该(编辑:几乎)永远不要尝试克隆您无法控制的对象或指针,因此可以免除规则:)
于 2010-07-28T07:32:50.987 回答
5

“深拷贝”与“浅拷贝”之间的区别作为实现细节是有意义的,但允许它泄漏超出通常表明有缺陷的抽象也可能以其他方式表现出来。

如果一个对象Foo持有一个对象引用纯粹是为了封装其中包含的对象的不可变方面,而不是 identity,那么正确的副本Foo可能包含引用的副本或对封装对象副本的引用。

如果一个对象Foo持有一个对象引用纯粹是为了封装一个对象的可变和不可变方面,而不是 identity,但是对该对象的引用永远不会暴露给任何会改变它的东西,同样的情况也适用。

如果一个对象Foo持有一个对象引用纯粹是为了封装一个对象的可变和不可变方面而不是 identity,并且所讨论的对象将被改变,那么正确的副本Foo必须包含对封装副本的引用目的。

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

如果一个对象Foo为了封装可变状态和对象身份而持有一个对象引用,则不可能单独生成正确的副本Foo。正确的副本Foo只能通过复制它所附加的整个对象集来生成。

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

于 2012-08-29T23:10:08.450 回答
2

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

void f( std::string s );

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

于 2010-07-28T07:48:30.037 回答
0

答案有点晚了,但是 c++11 或多或少地涵盖了你:

解决方案是,正如我在何时使用哪种指针的答案中 所详述的那样?,使用不同的指针类型来表达您拥有的(共享)所有权。

由于 astd::unique_ptr是不可复制的,您将被迫复制唯一指针所拥有的数据。根据成员所有权说明所有内容可能总是会明确在哪个成员上使用哪种副本。

于 2016-04-01T07:18:23.847 回答