【问题标题】:What is diffrence between lock() and expired()? weak_ptr C++lock() 和 expired() 有什么区别?弱点 C++
【发布时间】:2016-10-07 08:55:51
【问题描述】:

最近我开始学习 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();
    }
    

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

【问题讨论】:

  • 正如其他人所说,您必须使用 1 来避免比赛。简而言之,考虑 if (auto spFoo = wpFoo.lock()) { 从那时起(缺少 else 块)如果 spFoo 为 nullptr,则没有人可以访问它。

标签: c++ c++11 shared-ptr smart-pointers weak-ptr


【解决方案1】:

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

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

wp.expired() 仅用于执行“从缓冲区中删除每个过期的弱 ptr”之类的操作。对你说的目的没用。

每个弱指针一旦过期,仍然过期。但是,在您验证它已被接合后,接合的弱指针可能会立即过期。

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

&lt;&lt;--- here,我们对多线程环境中wpPtr 的状态一无所知。它可能已过期或未过期。另一方面:

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

&lt;&lt;--- there,我们确实知道弱指针已过期。

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

您有时可以确定自己几乎可以肯定不能尽早做到,这有时很有用,但在您尝试之前,您无法确定自己能做到。尝试可能会失败,此时您会处理错误。

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

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

if() 标头之外创建spFoo 是可以接受的,我更喜欢这种技术,因为spFoo 的范围仅限于它有效的区域。

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

auto spFoo = wpPtr.lock();

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

spFoo->DoSomething();

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

【讨论】:

    【解决方案2】:

    第二种变体有两个问题:

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

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

    【讨论】:

      【解决方案3】:

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

      w.use_count()w共享所有权的shared_ptr的数量

      w.expired() 如果w.use_count() 为零,则返回true,否则返回false

      w.lock() 如果expiredtrue,则返回空shared_ptr;否则返回一个shared_ptrw 指向的对象。

      (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
      }
      

      【讨论】:

        【解决方案4】:

        选项 1

        如果您选择选项 2,则在调用 wpPtr.expired() 和调用 wpPtr.lock() 之间,weak_ptr 可能已过期,spFoo-&gt;DoSomething() 行将尝试取消引用 null shared_ptr

        【讨论】:

          【解决方案5】:

          引用cppreference.com

          std::weak::lock 有效返回

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

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

          【讨论】:

          • 您删除了报价的重要部分。这使您的报价如此错误,我去检查以查看如何修复 cppreference。原来只是你的引用是错误的。
          • 我同意 Yakk 的观点。实际上,cppreference 的关键 点是您报告的行是原子地 执行的。这就是为什么原始问题中的选项 2 仅适用于非线程上下文。
          猜你喜欢
          • 2014-05-18
          • 1970-01-01
          • 2017-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-06
          • 2016-08-16
          • 2017-08-20
          相关资源
          最近更新 更多