将shared_ptr与托管语言引用进行比较

问题描述 投票:0回答:6

有人可以向 C++ 程序员解释 Java(以及 C#)引用和共享指针(来自 Boost 或来自 C++0x)之间最重要的区别吗?

我或多或少知道shared_ptr是如何实现的。我很好奇以下方面的差异:

1) 性能。 2)骑自行车。 shared_ptr 可以循环(A 和 B 持有指向彼此的指针)。 Java 可以骑自行车吗? 3)还有什么吗?

谢谢你。

c# java c++
6个回答
4
投票

性能

shared_ptr
表现得很好,但根据我的经验,效率比显式内存管理略低,主要是因为它是引用计数的,并且引用计数也必须分配。它的性能如何取决于很多因素,并且它与 Java/C# 垃圾收集器相比的性能只能根据每个用例来确定(取决于语言实现等因素)。

骑自行车只能使用

weak_ptr
,而不是使用两个
shared_ptr
。 Java让骑自行车不再费事;它的垃圾收集器将打破循环。我猜 C# 也会做同样的事情。

任何其他:一旦对

shared_ptr
指向的对象的最后一个引用超出范围,它就会被销毁。析构函数被立即调用。在Java中,终结器可能不会立即被调用。我不知道 C# 在这一点上表现如何。


3
投票

关键区别在于,当共享指针的使用计数变为零时,它指向的对象将立即被销毁(调用析构函数并释放对象)。在 Java 和 C# 中,对象的释放会被推迟,直到垃圾收集器选择释放对象(即,它是不确定的)。

关于周期,我不确定我理解你的意思。在 Java 和 C# 中,两个包含彼此引用的成员字段的对象很常见,从而创建一个循环。例如,汽车和引擎 - 汽车通过引擎字段引用引擎,引擎可以通过汽车字段引用它的汽车。


2
投票

没有人指出内存管理器在托管内存中移动对象的可能性。因此,在 C# 中,没有简单的引用/指针,它们的工作方式类似于描述管理器返回的对象的 ID。
在 C++ 中,您无法使用 shared_ptr 实现此目的,因为该对象在创建后仍保留在同一位置。


1
投票

首先,Java/C# 只有指针,没有引用,尽管他们这样称呼它们。引用是 C++ 独有的功能。 Java/C# 中的垃圾收集基本上意味着无限的生命周期。另一方面,当计数变为零时,

shared_ptr
提供共享和确定性破坏。因此,
shared_ptr
可以用来自动管理任何资源,而不仅仅是内存分配。从某种意义上说(就像任何 RAII 设计一样),它将指针语义转变为更强大的值语义。


1
投票

C++ 引用计数指针的循环引用将不会被释放。您可以使用弱指针来解决这个问题。当垃圾收集器愿意时,Java 或 C# 中的循环引用可能会被释放。

当 C++ 引用计数指针中的计数降至零时,将调用析构函数。当 Java 对象不再可访问时,其终结器可能不会立即或永远不会被调用。因此,对于需要显式处置外部资源的对象,需要某种形式的显式调用。


0
投票

C++

shared_ptr
与 Java 或 C# 中的其他语言之间存在根本区别。

C++
shared_ptr
基于模板

该对象不包含

use_count
,但
shared_ptr
包含它。

优点
  • 我们可以将任何物体放入
    shared_ptr
缺点
  • 如果我们将同一个对象放入两个
    shared_ptr
    中,那么我们就有两个
    use_count
    。如果其中一个变为0,则其他shared_ptr包含一个悬空指针。
    • 为了避免这种情况,他们引入了
      weak_ptr
      。 (很乱)

C# 在对象本身中具有隐式 refCnt

C++ CLI 语法有一个关键字

ref
来指示一个类被垃圾收集(托管)。

class ref ManagedObj
{
};
优点
  • 不需要
    weak_ptr
缺点
  • 我们只能将托管对象放入托管指针中。
  • 可以存在循环引用。

底线

我的理解是C++可能会走向C#的垃圾收集方式。

[[ ref ]] class ManagedObj
{
};

template <typename T, std::refEmbedded<T>>
class shared_ptr { /* old impl */ int use_count; };

template <typename T, true>
class shared_ptr { /* new impl */ };

© www.soinside.com 2019 - 2024. All rights reserved.