shared_ptr和weak_ptr之间缺少相等性

11

虽然我理解为什么shared_ptrunique_ptr没有operator==,但我想知道为什么shared_ptrweak_ptr也没有。尤其是因为您可以通过对shared_ptr的引用来创建weak_ptr

我认为在99%的情况下,您希望使用lhs.get() == rhs.get()进行比较。除非有人能告诉我一个好的理由不这样做,否则我会在我的代码中引入这个操作符。


3
但是weak_ptr没有get方法。 - CB Bailey
3个回答

18
weak_ptr没有get()方法,因为你需要在访问底层指针之前显式地锁定weak_ptr。这样做是一个有意的设计决策。如果转换是隐式的,那么很容易编写不安全的代码,如果最后一个指向该对象的shared_ptr在仍在检查从weak_ptr获得的底层指针时被销毁。

这个boost页面对陷阱和为什么weak_ptr具有如此有限的接口有很好的描述。

如果您需要快速比较,则可以执行shared == weak.lock()。如果比较结果为true,则知道weak必须仍然有效,因为您持有到相同对象的单独shared_ptr。如果比较返回false,则没有这样的保证。


在代码的较大部分调用lock确实更有意义。这是一个好的设计决策。 - abergmeier
调用 lock 是不必要的昂贵操作,也不能提供稳定的比较。请参见我的答案以获取解决方案。 - rdb

4

因为它需要成本。

weak_ptr像观察者,而不是真正的指针。要使用它进行任何工作,您首先需要使用其lock()方法从中获取一个shared_ptr

这具有获取所有权的效果,但与复制常规shared_ptr一样费用高昂(计数增加等...),因此这并不是微不足道的。

因此,通过不提供==,您被迫退后,并实际检查您是否真的需要它。


2
我不确定比较相等性是否需要关心有效性,因此可以便宜地实现:如果weak_ptr引用相同的实体,则它必须是有效的(或为0),否则我们不关心它是否有效。 - Konrad Rudolph
@KonradRudolph:也许是,也许不是。但是由于通常情况下获取指针是不安全的(因为您将拥有指针但无法访问指针),因此您无法从weak_ptr获取指针,被迫通过shared_ptr进行操作。所以... - Matthieu M.
1
不,只要在内部进行,获取指针是安全的。 - Konrad Rudolph
@KonradRudolph:啊,我明白你的意思了。是的,按照现在的实现方式,在内部是可能的。然而,这将限制shared_ptrweak_ptr的实现方式,只适用于相当有限的用例。 - Matthieu M.

3
正如其他答案所指出的那样,简单比较基础指针是危险的。考虑以下情况:存在一个弱引用A指向一个对象,该对象随后被删除,因此弱引用过期。然后,在由该删除释放的内存中分配了另一个具有相同地址的对象。现在,即使弱指针最初引用不同的对象,底层指针也是相同的!
正如其他答案所建议的那样,一种方法是比较“shared == weak.lock()”。由于如果弱指针过期,“lock()”将返回“nullptr”(而不是一些虚假指针),因此可以用来确定它们是否相等(只要“shared != nullptr”)。但是,这有两个问题:
- 当弱指针过期时,它将停止工作,在这种情况下,比较会发生变化;在过期后,它只会在“shared == nullptr”时返回true。在必须保持稳定的情况下使用它作为“unordered_map”或“unordered_set”中的键时,这可能是危险的。 - “lock()”是一个相对昂贵的操作。
幸运的是,有更好的方法来解决这个问题。 “weak_ptr”和“shared_ptr”都存储指向所谓的“控制块”的指针,该控制块存储引用计数并在引用仍然存在时保留原始对象。要检查它们是否指向同一对象,我们只需要比较控制块指针即可。这可以使用“owner_before”方法完成:
template<class T>
bool owner_equals(std::shared_ptr<T> &lhs, std::weak_ptr<T> &rhs) {
    return !lhs.owner_before(rhs) && !rhs.owner_before(lhs);
}

这种方法甚至适用于比较两个std :: weak_ptr, 如果您想知道它们(曾经)是否引用了同一个对象,因为控制块将持续(至少)与所有弱引用一样长时间。请注意,如果您正在使用std :: shared_ptr的别名特性,则可能无法产生预期的结果。这是一种让您创建两个具有相同控制块但存储不同指针的std :: shared_ptr实例的功能。

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