什么时候需要浅拷贝(而不是深拷贝)?

有人可以给出一个需要浅拷贝的情况的例子吗?

请注意,在某些情况下,浅拷贝和深拷贝是相同的。 当对象没有对其任何子变量的所有权时,就会发生这种情况; 也就是说,所有子变量都是聚合的 。 我想看一个例子,其中一个对象是由它拥有的变量组成的 ,并且仍然希望将它们浅浅地复制。

备注 :我不关心给出示例的语言。 我从C ++ / Java / C#的角度来问这个问题,虽然我认为复制是一种与语言无关的概念。

当拥有的变量是不可变类型时,浅拷贝就足够了。 可以进行深层复制,但只会导致额外的内存使用。

一个可能的用例是组合对象可以从浅拷贝中存在的状态信息派生,例如,您希望序列化您的对象。

您可以存储浅拷贝,然后在反序列化时从状态重建完整对象。

来自C ++ POV,将有一个场景,我将使用浅拷贝:实现写时复制机制。

class Element{ } class Example { private List elementList = new ArrayList getElementList() { return new ArrayList(this.elementList); } public void addElement(Element e) { elementList.add(e); } public void removeElement(Element e) { elementList.remove(e); } } 

您希望Example.elementList所有修改都由对象方法完成,但您希望公开存储在列表中的元素。 所以你创建了一个返回浅拷贝的getter。 复制,因为您不希望调用者修改对象的列表和浅层,因为您希望从列表中公开对象,而不是它们的副本。

如果你看看Gang of Four设计模式,就会有一个名为FlyWeight的模式。 这是维基百科的一句话:

flyweight是一个通过与其他类似对象共享尽可能多的数据来最小化内存使用的对象; 当简单的重复表示使用不可接受的内存量时,它是一种大量使用对象的方法。

这是浅拷贝的可接受用途。

正如我在回答您的相关问题时指出的那样,许多语言并不具备浅层和深层复制的概念。 但是,C ++中的这个类会阻止您使用NULL指针,这可能是需要“浅”副本的示例:

 template  struct NotNull { T * p; NotNull( T * t ) : p( t ) {} T & operator *() { if ( ! p ) { throw "null!"; } return * p; } }; 

该类不拥有指向的东西,因此不应该复制。

我对此的看法是,对于C ++与Java,使用深层复制的情况不同。

  • 在C ++中,您可以使用深度复制来避免困难的内存管理问题,或者使其更容易实现multithreading应用程序。

  • 在Java中,由于这些原因,不需要深度复制。 由于这些语言都是垃圾收集的,因此没有必要通过箍来释放数据结构。 因此,不需要深层复制来简化问题。 类似地,Java内置支持将多个线程的访问同步到共享数据结构。

在大多数情况下,假设您的应用程序的数据结构设计得很好,Java中不需要深度复制。

浅拷贝可以更快地制作,这通常是出于显而易见的原因。 最浅层的复制显然只是对原始对象的引用的副本。 如果您没有变异对象并且可以保证在使用它时不会以其他方式进行更改,那么副本越浅越好。

备注:我不关心给出示例的语言。 我从C ++ / Java / C#的角度来问这个问题,虽然我认为复制是一种与语言无关的概念。

我认为这在很大程度上取决于语言。

在C ++中,复制扮演的角色与Java / C#中的角色完全不同。 在C ++中,我想不出很多浅层复制有意义的情况。 您通常只是创建对象的引用或指针。 复制构造函数通常实现深层复制,因为在C ++中,对象获取其成员的所有权,并负责管理其生命周期。 因为没有GC,这些所有权语义变得很重要,你不能只有两个对象指向第三个没有特别小心(他们是否使用引用计数来保持第三个存活?如果没有,两个对象中的哪一个拥有它? )

我甚至会说“深”和“浅”副本之间的区别在C ++中并没有真正意义。 副本会创建新创建的对象所需的所有内容。 有时,它共享一些数据(通常不是任何一个对象所拥有的数据),因此它可能不是“真正的”深层副本“,但它也不浅,因为通常复制该类的大部分内容。

Interesting Posts