lock() 和 expired() 有什么区别? weak_ptr C++

What is diffrence between lock() and expired()? weak_ptr C++

最近我开始学习 C++11。 我研究了大约 weak_ptr。有两种获取原始指针的方法。

  1. lock()函数

    shared_ptr<Foo> spFoo = wpPtr.lock();
    
    if(spFoo) {
        spFoo->DoSomething();
    }
    
  2. expired()函数

    if(!wpPtr.expired())
    {
        shared_ptr<Foo> spFoo = wpPtr.lock();
        spFoo->DoSomething();
    }
    

哪种方法更好?这两种方式有什么不同?

引用自cppreference.com

std::weak::lock 有效 returns

expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

使用 expired 检查底层对象是否有效并锁定以潜在地将对象提升为 std::shared_ptr

选项 1.

如果您选择选项 2,那么在调用 wpPtr.expired() 和调用 wpPtr.lock() 之间 weak_ptr 可能已经过期并且行 spFoo->DoSomething() 将尝试取消引用空 shared_ptr.

第二个变体有两个问题:

  1. 它进行了不必要的检查wpPtr.expired()
  2. 取消引用前缺少必要的检查 if (spFoo) spFoo

第一个变体是事务性的,当您最终需要使用弱指针引用的对象时使用该变体。

所以 shared ptr 和 weak ptr 是线程安全的,因为如果你有一个给定线程本地的 object 实例,并且它们共享一个公共的 pointed-to object ,您可以在一个线程和另一个线程中与他们交互,一切正常。

要使其正常工作,您必须正确使用它们。

wp.expired() 仅对执行诸如“从缓冲区中删除每个过期的弱指针”之类的事情有用。它对您提出的目的没有用。

每个弱指针一旦过期,就会保持过期状态。但是,一个已启用的弱指针在您确认它已启用后可能会立即过期。

if(!wpPtr.expired())  {
  // <<--- here
  shared_ptr<Foo> spFoo = wpPtr.lock();
  spFoo->DoSomething();
}

<<--- here,我们对 multi-threaded 环境中 wpPtr 的状态一无所知。它可能已过期或未过期。另一方面:

if(wpPtr.expired())  {
  // <<--- there
}

<<--- there 我们 知道弱指针已过期。

与文件 io 和其他类型的“事务性”操作一样,检查您是否可以做某事的唯一方法是尝试去做。在确定你应该能够做到这一点和做到这一点之间,状态可能会改变并且操作可能会失败。

您有时会发现您几乎肯定无法尽早做到,这有时很有用,但您不能确定自己是否可以做到,除非您尝试过。尝试尝试可能会失败,此时您处理错误。

if(auto spFoo = wpPtr.lock())  {
  spFoo->DoSomething();
}

这是与弱指针交互的“正确”方式。测试弱指针的有效性,并在同一操作中获取共享指针。

if() header 之外创建一个 spFoo 是可以接受的,我更喜欢这种技术,因为 spFoo 的范围完全限于它所在的区域有效。

另一种首选技术是提前退出:

auto spFoo = wpPtr.lock();

if(!spFoo) return error("wp empty");

spFoo->DoSomething();

这使得代码的“预期”执行在一条直线上流动,没有缩进、条件或跳转。

下面是weak_ptr的相关操作。你应该选择 选项 1 因为方法 2 不是线程安全的。

w.use_count() The number of shared_ptrs that share ownership with w

w.expired() returns true if w.use_count() is zero, false otherwise

w.lock() If expired is true, returns a null shared_ptr; otherwise returns a shared_ptr to the object to which w points.

(2) 不是线程安全的

// let p be the last shared_ptr pointing at the same object as wpPtr
if (!wpPtr.expired())
{
    // we enter the if-statement because wpPtr.use_count() is 1
    // p goes out of scope on its thread, the object gets deleted
    shared_ptr<Foo> spFoo = wpPtr.lock(); // null shared_ptr
    spFoo->DoSomething(); // ERROR! deferencing null pointer
}

(1) 线程安全

// let p be the last shared_ptr pointing at the same object as wpPtr
shared_ptr<Foo> spFoo = wpPtr.lock();
// now, wpPtr.use_count() is 2, because spFoo and p are both pointing at the object
// p goes out of scope on its thread, but spFoo is still pointing at the object
if(spFoo) {
    spFoo->DoSomething(); // OK! safe to dereference
}