为什么不能从 unique_ptr 构造 weak_ptr?

Why can't a weak_ptr be constructed from a unique_ptr?

如果我理解正确,weak_ptr 不会增加托管对象的引用计数,因此它不代表所有权。它只是让您访问一个对象,该对象的生命周期由其他人管理。 所以我真的不明白为什么 weak_ptr 不能从 unique_ptr 构造,而只能从 shared_ptr.

构造

谁能简单解释一下?

std::weak_ptr只有通过lock()转换成std::shared_ptr才能使用。如果标准允许您的建议,则意味着您需要将 std::weak_ptr 转换为唯一性才能使用它,这违反了唯一性(或重新发明 std::shared_ptr

为了说明,看两段代码:

std::shared_ptr<int> shared = std::make_shared<int>(10);
std::weak_ptr<int> weak(shared);

{
*(weak.lock()) = 20; //OK, the temporary shared_ptr will be destroyed but the pointee-integer still has shared  to keep it alive
}

现在听取您的建议:

std::unique_ptr<int> unique = std::make_unique<int>(10);
std::weak_ptr<int> weak(unique);

{
*(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it! 
}

已经说了,你可能会建议只有一个unique_ptr,你仍然可以解引用weak_ptr(而不创建另一个unique_ptr)那么就没有问题。但是,unique_ptrshared_ptr 有一个引用有什么区别呢?或者,常规 unique_ptr 和使用 get 获取的 C 指针之间有什么区别?

weak_ptr 不适用于 "general nonowning resources",它有一个非常具体的工作 - weak_ptr 的主要目标是防止 shared_ptr 的循环指向内存泄漏。其他任何事情都需要用普通 unique_ptrshared_ptr.

来完成

如果您考虑一下,weak_ptr 必须引用对象本身以外的其他内容。那是因为该对象可能不再存在(当没有更多的强指针指向它时)并且 weak_ptr 仍然必须引用包含该对象不再存在的信息的内容。

有了shared_ptr,那个东西就是包含引用计数的东西。但是对于unique_ptr,没有引用计数,所以没有包含引用计数的东西,因此当对象消失时没有任何东西继续存在。所以weak_ptr没有什么可参考的。

使用这样的 weak_ptr 也没有明智的方法。要使用它,您必须有一些方法来保证该对象在您使用它时没有被破坏。使用 shared_ptr 很容易——这就是 shared_ptr 的作用。但是你如何用 unique_ptr 做到这一点?你显然不能拥有其中两个,并且其他东西必须已经拥有该对象,否则它会因为你的指针很弱而被破坏。

从概念上讲,没有什么可以阻止 weak_ptr 仅提供访问权限而 unique_ptr 控制生命周期的实现。但是,这有问题:

  • unique_ptr 一开始不使用引用计数。添加用于管理弱引用的管理结构是可能的,但需要额外的动态分配。由于 unique_ptr 应该避免对原始指针的任何(!)运行时开销,因此该开销是不可接受的。
  • 为了使用一个weak_ptr引用的对象,需要从中提取一个"real"引用,它会先验证指针没有过期,然后给你这个真实参考(在本例中为 shared_ptr)。这意味着您突然对一个应该被唯一拥有的对象有了第二个引用,这是错误的根源。这不能通过返回一个混合的半强指针来解决,它只会暂时延迟指针对象的可能破坏,因为你也可以存储那个指针,打败 unique_ptr.[=23= 背后的想法]

只是想知道,你想在这里使用 weak_ptr 解决什么问题?

一个shared_ptr基本上有两部分:

  1. 指向的对象
  2. 引用计数对象

一旦引用计数降为零,对象 (#1) 就会被删除。

weak_ptr 需要知道对象 (#1) 是否仍然存在。为了做到这一点,它必须能够看到引用计数对象 (#2),如果它不为零,它可以为该对象创建一个 shared_ptr(通过增加引用计数)。如果计数为零,它将 return 一个空 shared_ptr.

考虑一下什么时候可以删除引用计数对象(#2)的问题?我们必须等到没有 shared_ptrweak_ptr 对象引用它。为此,引用计数对象持有 两个 引用计数,一个 strong ref 和一个 weak ref。只有当这两个计数都为零时,引用计数对象才会被删除。这意味着部分内存只有在所有弱引用都消失后才能被释放(这意味着一个hidden disadvantage with make_shared)。

tl;dr; weak_ptr 取决于 弱引用计数 ,它是 shared_ptr 的一部分,没有 shared_ptr.

就不可能有 weak_ptr

还没有人提到问题的性能方面,所以让我投入 0.02 美元。

weak_ptr 必须以某种方式知道相应的 shared_ptr 何时全部超出范围并且指向的对象已被释放和销毁。这意味着 shared_ptrs 需要以某种方式将对每个 weak_ptr 的破坏传达给同一个对象。这有一定的成本——例如,需要更新全局哈希 table,其中 weak_ptr 从中获取地址(或者 nullptr 如果对象被销毁)。

这还涉及在多线程环境中进行锁定,因此对于某些任务来说它可能太慢了。

但是,unique_ptr 的目标是提供零成本 RAII 风格的抽象class。因此,除了 deleteing(或 delete[]ing)动态分配的对象之外,它不应产生任何其他成本。例如,通过锁定或以其他方式保护的哈希 table 访问所造成的延迟可能与释放成本相当,但是,在 unique_ptr.[=20= 的情况下,这是不可取的。 ]

区分 unique_ptr 优于 shared_ptr 的原因可能很有用。

性能 一个明显的原因是计算时间和内存使用。按照目前的定义,shared_ptr 对象通常需要引用计数值之类的东西,它需要 space 并且还必须积极维护。 unique_ptr 物体没有。

语义 对于unique_ptr,作为程序员,您很清楚指向的对象何时会被销毁:当unique_ptr 被销毁或调用其修改方法之一时。在大型或不熟悉的代码库上,使用 unique_ptr 静态传达(并强制执行)有关程序运行时行为的一些信息,这些信息可能并不明显。

上面的评论通常集中在基于性能的原因上,即不希望 weak_ptr 对象绑定到 unique_ptr 对象。但是人们可能想知道基于语义的论点是否足以让 STL 的某些未来版本支持原始问题所暗示的用例。

看起来每个人都在这里写 std::weak_ptr 而不是关于弱指针概念,我相信这正是作者所要求的

我认为没有人提到为什么标准库不为 unique_ptr 提供 weak_ptr。弱指针概念不否认 unique_ptr 用法。如果对象已经被删除,弱指针只是一个信息,所以它不是魔术,而是非常简单的通用观察者模式。

是因为线程安全和与shared_ptr的一致性。

您只是不能保证您的 weak_ptr(从存在于其他线程上的 unique_ptr 创建)在调用指向对象的方法期间不会被销毁。 因为 weak_ptr 需要和 std::shared_ptr 保持一致,保证线程安全。您可以实现 weak_ptr ,它可以与 unique_ptr 一起正常工作,但只能在同一个线程上 - 在这种情况下不需要锁定方法。您可以检查 base::WeakPtr 和 base::WeakPtrFactory 的铬源 - 您可以在 unique_ptr 中自由使用它。 Chromium 弱指针代码很可能是基于最后一个成员的破坏——你需要将工厂添加为最后一个成员,然后我相信 WeakPtr 被告知对象删除(我不是 100% 确定)——所以它看起来不很难实施。

总体上使用 unique_ptr 和弱指针概念是可以恕我直言。

经过多年的c++编程工作,我终于意识到在c++世界中正确的方法不是永远使用shared_ptr/weak_ptr。我们花了很多时间来修复由于 share_ptr.

归属不明导致的错误

解决方案是使用 unque_ptr 和 unique_ptr 的一些弱指针,就像本主题中预期的那样。

cpp 标准库没有 unique_ptr 的弱指针,所以我在这里创建了一个非常简单但有用的库 --

https://github.com/xhawk18/noshared_ptr

它有两个新的智能指针,

noshared_ptr<T>, a new kind of unique_ptr
noweak_ptr<T>, the weak pointer for noshare_ptr<T>