【发布时间】:2011-06-02 00:47:31
【问题描述】:
谁能向 C++ 程序员解释 Java(以及 C#)引用和 shared_ptr(来自 Boost 或来自 C++0x)之间的最重要区别。
我或多或少知道 shared_ptr 是如何实现的。我很好奇以下方面的差异:
1) 性能。 2) 骑自行车。 shared_ptr 可以循环(A 和 B 相互保存指针)。在 Java 中可以循环吗? 3) 还有什么?
谢谢。
【问题讨论】:
谁能向 C++ 程序员解释 Java(以及 C#)引用和 shared_ptr(来自 Boost 或来自 C++0x)之间的最重要区别。
我或多或少知道 shared_ptr 是如何实现的。我很好奇以下方面的差异:
1) 性能。 2) 骑自行车。 shared_ptr 可以循环(A 和 B 相互保存指针)。在 Java 中可以循环吗? 3) 还有什么?
谢谢。
【问题讨论】:
没有人指出内存管理器在托管内存中移动对象的可能性。所以在 C# 中没有简单的引用/指针,它们的工作方式类似于描述由管理器返回的对象的 ID。
在 C++ 中,您无法使用 shared_ptr 来实现此目的,因为对象在创建后仍保留在同一位置。
【讨论】:
不会释放带有 C++ 引用计数指针的循环引用。您可以使用弱指针来解决此问题。 Java 或 C# 中的循环引用可能会在垃圾收集器喜欢时被释放。
当 C++ 引用计数指针中的计数降至零时,将调用析构函数。当一个 Java 对象不再可访问时,它的终结器可能不会立即或永远不会被调用。因此,对于需要显式处理外部资源的对象,需要某种形式的显式调用。
【讨论】:
首先,Java/C# 只有指针,没有引用,尽管它们是这样称呼的。引用是一个独特的 C++ 特性。 Java/C# 中的垃圾收集基本上意味着无限的生命周期。另一方面,shared_ptr 在计数变为零时提供共享和确定性销毁。因此,shared_ptr 可用于自动管理任何资源,而不仅仅是内存分配。在某种意义上(就像任何 RAII 设计一样),它将指针语义转化为更强大的值语义。
【讨论】:
主要区别在于,当共享指针的使用计数变为零时,它指向的对象立即被销毁(调用析构函数并释放对象)。在 Java 和 C# 中,对象的释放被推迟,直到垃圾收集器选择释放对象(即,它是不确定的)。
关于周期,我不确定我理解你的意思。在 Java 和 C# 中,拥有两个包含相互引用的成员字段的对象是很常见的,从而创建了一个循环。例如汽车和引擎 - 汽车通过引擎字段引用引擎,引擎可以通过汽车字段引用其汽车。
【讨论】:
性能:shared_ptr 性能相当不错,但根据我的经验,它的效率略低于显式内存管理,主要是因为它是引用计数的,而且引用计数也必须分配。它的性能有多好取决于很多因素,它与 Java/C# 垃圾收集器相比的好坏只能根据每个用例来确定(取决于其他因素中的语言实现)。
骑自行车只能使用weak_ptr,不能使用两个shared_ptrs。 Java 允许毫不费力地骑自行车;它的垃圾收集器将break the cycles。我的猜测是 C# 也是如此。
其他任何东西:shared_ptr 指向的对象在最后一次引用超出范围后立即被销毁。立即调用析构函数。在 Java 中,可能不会立即调用终结器。我不知道 C# 在这一点上的表现如何。
【讨论】:
make_shared (boost.org/doc/libs/release/libs/smart_ptr/make_shared.html),那么引用计数和被引用对象都分配在一个块中。
shared_ptr 对性能的最大影响是引用计数器的联锁递增/递减
using 强制执行此操作。