虽然我确实理解为什么没有operator==
for shared_ptr
and unique_ptr
,但我想知道为什么没有 for shared_ptr
and weak_ptr
。特别是因为您可以weak_ptr
通过对shared_ptr
. 我会假设 99% 的时间都是你想要的lhs.get() == rhs.get()
。我现在将继续并将其引入我的代码中,除非有人可以给我一个很好的理由,为什么不应该做这样的事情。
3 回答
weak_ptr
没有get()
方法,因为您需要显式锁定weak_ptr
才能访问底层指针。明确这一点是一个深思熟虑的设计决定。如果转换是隐式的,那么很容易编写不安全的代码,如果shared_ptr
对象的最后一个要被销毁,而从 中获得的底层指针weak_ptr
仍在检查中。
这个提升页面很好地描述了陷阱以及为什么weak_ptr
界面如此有限。
如果您需要进行快速比较,那么您可以执行shared == weak.lock()
. 如果比较是真的,那么你知道它weak
必须仍然有效,因为你持有一个单独shared_ptr
的对象。如果比较返回 false,则没有这样的保证。
因为它是有代价的。
Aweak_ptr
就像一个观察者,而不是一个真正的指针。要使用它进行任何工作,您首先需要shared_ptr
使用它的lock()
方法从中获取 a 。
这具有获得所有权的效果,但它与复制常规shared_ptr
(计数增量等)一样昂贵,所以它不是微不足道的。
因此,如果不提供==
,您将被迫退后一步并实际检查您是否真的需要这个。
正如其他答案所指出的那样,简单地比较底层指针是危险的。例如,考虑以下场景:一个对象的弱引用 A 存在,该对象随后被删除,因此弱引用过期。然后,在由所述删除释放的内存中分配另一个对象,该对象具有相同的地址。现在底层指针是相同的,即使弱指针最初指向不同的对象!
正如其他答案所建议的那样,一种方法是比较shared == weak.lock()
. 由于如果弱指针过期lock()
将返回nullptr
(而不是一些虚假指针),因此他的工作是识别它们是否相等(只要shared != nullptr
)。但是,这样做有两个问题:
- 当弱指针过期时它停止工作,在这种情况下比较会改变;过期后,只有在
shared == nullptr
. 这在比较必须保持稳定的情况下可能很危险,例如将其用作unordered_map
or中的键时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
s,因为控制块将(至少)与所有弱引用一样长。
请记住,如果您使用 的别名功能,这可能不会产生预期的结果std::shared_ptr
,该功能允许您std::shared_ptr
使用相同的控制块创建两个实例,但存储不同的指针。