Dim*_* C. 13 language-agnostic copy deep-copy shallow-copy
昨天我问了一个关于在C#中复制对象的问题,大多数答案都集中在深拷贝和浅拷贝之间的区别,以及应该弄清楚两个拷贝变体中的哪一个给定拷贝构造函数(或运算符或函数)的事实.实现.我发现这很奇怪.
我用C++编写了很多软件,这种语言很大程度上依赖于复制,我从来不需要多种复制变体.我用过的唯一一种复制操作是我称之为" 足够深的复制 ".它执行以下操作:
现在,我的问题有三个:
对象只需要复制需要复制的内容.虽然这个问题标记为语言不可知,并且你提到了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;
}
};
Run Code Online (Sandbox Code Playgroud)
使用此对象,需要处理两个字符串缓冲区.第一个bar是由类本身创建和管理的.克隆对象时,应分配新缓冲区.
baz另一方面,不应该.事实上,你不能,因为你没有足够的信息这样做.应该只复制指针.
当然,foo这只是一个数字.只需复制它,没有别的担心:)
总之,直接回答您的问题:
"深拷贝"与"浅拷贝"之间的区别作为一个实现细节是有意义的,但允许它泄漏超出通常表明有缺陷的抽象,这可能也会以其他方式表现出来.
如果对象Foo仅仅为了封装包含在其中的对象的不可变方面(除了标识之外)而保持对象引用,则正确的副本Foo可以包含引用的副本或对封装对象的副本的引用.
如果一个对象Foo持有一个对象引用,纯粹是出于封装除了identity之外的对象的可变和不可变方面的目的,但是对该对象的引用不会暴露于任何会使其变异的东西,同样的情况也适用.
如果一个对象Foo持有一个对象引用纯粹是为了封装除了identity之外的对象的可变和不可变方面,并且所讨论的对象将要进行变异,那么正确的副本Foo必须包含对封装副本的引用宾语.
如果一个对象Foo持有一个对象引用纯粹是为了封装对象的不可变方面,包括标识,那么正确的副本Foo必须包含引用的副本; 它不能包含对重复对象的引用.
如果对象Foo持有对象引用以封装可变状态和对象标识,则无法单独生成正确的副本Foo.正确的副本Foo只能通过复制它所附着的整个对象集来产生.
谈论"浅拷贝"的唯一时间是将不完整的操作用作制作正确拷贝的步骤之一.否则,只有一个正确的副本"深度",由对象引用中封装的状态类型控制.
小智 2
大多数 C++ 程序员不使用术语“浅复制”和“深复制”,因为通常只有一种方法来复制对象。在 C++ 中尤其如此,因为编译器在许多情况下使用复制构造函数,程序员可以告诉它使用哪个复制构造函数 - 例如:
void f( std::string s );
Run Code Online (Sandbox Code Playgroud)
无法告诉编译器如何复制字符串。