何时需要浅拷贝(而不是深拷贝)?

10

有没有人能给出需要使用浅拷贝的情况的例子?

需要注意的是,在某些情况下,浅拷贝和深拷贝是相同的。当对象对其所有子变量没有所有权时,即所有子变量都是聚合时,就会发生这种情况。我希望看到一些例子,其中一个对象由它拥有的变量组成,并且仍然希望以浅层次进行复制。

备注:我不在意例子所使用的语言。我从C++/Java/C#的角度提出这个问题,尽管我认为复制是一种与语言无关的概念。

9个回答

8

当所拥有的变量为不可变类型时,浅复制就足够了。深度复制是可能的,但只会增加内存使用。


确实!虽然这是一种懒复制的特殊情况,所以我与YeenFei的评论相同:从概念上讲,它仍然是深复制。 - Dimitri C.

3
class Element{
}

class Example {
    private List<Element> elementList = new ArrayList<Element();

    public List<Element> getElementList() {
        return new ArrayList<Element>(this.elementList);
    }

    public void addElement(Element e) {
        elementList.add(e);
    }

    public void removeElement(Element e) {
        elementList.remove(e);
    }
}

你希望所有对 Example.elementList 的修改都通过对象方法进行,但是你想要暴露存储在列表中的元素。因此,你创建了一个 getter 方法来返回一个浅拷贝。浅拷贝是因为你想要暴露列表中的对象,而不是它们的副本,但同时也是拷贝,因为你不希望调用者修改对象的列表。

3

一个可能的使用情况是,当组合对象可以从浅拷贝中存在的状态信息派生出来,并且您希望序列化对象时。

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


这是一个很好的例子!谢谢。 - Dimitri C.
顺便问一下,您是否遇到过无法或不希望复制未拥有的数据,从而序列化悬空指针的情况? - Dimitri C.
外部资源(例如数据库连接)是“不可复制的”。但这并不意味着您必须存储悬空指针,您可以通过在序列化之前释放资源来为此做好准备。 - Vinko Vrsalovic
我所说的“悬空指针”是指一种悬空序列化引用。 - Dimitri C.

3

从C++的角度来看,我会在实现写时复制机制时使用浅拷贝。


是的,懒惰复制是另一个很好的例子,谢谢!尽管在概念上,它仍然是深拷贝,当然 :p - Dimitri C.

3
如果您查看四人组设计模式,会发现有一种叫做FlyWeight的模式。以下是维基百科上的一句话引用:
“轻量级模式是指通过与其他类似对象共享尽可能多的数据来最小化内存使用的对象;这是在大量使用对象时使用的一种方式,当简单重复表示将使用不可接受的内存量时。”
这将是浅拷贝的可接受用途。

2

正如我在回答你相关问题时指出的那样,许多语言实际上没有浅复制和深复制的概念。然而,在C++中,这个类防止你使用空指针的情况可能是需要“浅”复制的一个例子:

template <typename T>
struct NotNull {

   T * p;

   NotNull( T * t ) : p( t ) {}

   T & operator *() { 
       if ( ! p ) {
          throw "null!";
       }
       return * p;
   }
};

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

2
我的看法是,在C++和Java中使用深拷贝的情况是不同的。
在C++中,您可能会使用深拷贝来避免复杂的内存管理问题,或者使实现多线程应用程序更容易。
在Java中,出于这些原因,不需要进行深度复制。由于这两种语言都是垃圾回收的,因此不需要费力地释放数据结构。因此,深度复制并不是简化问题所必需的。类似地,Java具有内置支持,可以同步访问多个线程对共享数据结构的访问。
在大多数情况下,假设您的应用程序数据结构设计良好,Java中不需要进行深度复制。

1

浅复制速度更快,这通常是很有吸引力的显而易见的原因。浅复制的最终目的显然就是原始对象的引用拷贝。如果你不需要改变一个对象并且可以保证在处理对象时它不会被其他方式修改,那么复制的越浅,越好。


1
我认为这取决于编程语言。
在C++中,复制的作用与Java/C#中有很大不同。在C++中,我想不出许多情况下浅复制是有意义的。你通常只需创建对象的引用或指针。复制构造函数通常实现深度复制,因为在C++中,一个对象拥有其成员,并负责管理它们的生命周期。由于没有垃圾回收,这些所有权语义变得重要,不能仅仅有两个对象指向第三个对象而不加特殊处理(它们是否使用引用计数来保持第三个对象的存活?如果没有,哪个对象拥有它?)
我甚至会说,“深”和“浅”复制之间的区别在C++中并没有真正意义。复制创建了新创建的对象所需的一切。有时,它共享一点数据(通常是两个对象都不拥有的数据),因此它可能不是“真正”的深度复制”,但它也不是浅复制,因为类的大部分通常被复制。

有趣的话。你说得很对,C++非常依赖于深拷贝,而所有权比在C#或Java中更重要。然而,在C#和Java中也需要深拷贝,尽管这种情况发生得更少。我认为,在C#或Java中,人们更容易实现浅拷贝,但我怀疑这是因为实现深拷贝更麻烦,并且在大多数情况下,浅拷贝就足够了。 - Dimitri C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接