【问题标题】:In well designed code should you expect locking of weak_ptr to always succeed?在精心设计的代码中,您是否应该期望锁定 weak_ptr 总是成功?
【发布时间】:2012-06-12 11:52:52
【问题描述】:

当您希望使用弱指针进行访问时,首先建议您通过锁定获得指向对象的强指针。如果指向的对象之前被删除,锁定可能不会成功。

在我看来,除非你在打破循环以确定什么是弱指针时做错了什么,否则锁定将会成功。所以你锁定只是为了交叉检查你的设计。

这是正确的吗?

我看到了一些关于缓存的评论,但它们似乎是对weak_ptrs的滥用。但当然,一个人的虐待是另一个人的创新。我想听听意见。

【问题讨论】:

  • 这是一个很好的问题,不幸的是堆栈溢出不适用于征求意见的问题。这里是题外话。
  • 请注意,如果您认为将weak_ptr提升为shared_ptr总是会成功,并且如果您还认为如此提升的weak_ptr永远不会是最后一个引用(也就是说,无论您设计什么强引用在,你设计的只要你持有你的锁就可以持续),那么你也可以使用原始指针而不是weak_ptr。 可以使用原始指针的代码中的错误检测是一个使用弱指针的动机,但我不认为这是他们发明的动机。跨度>

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


【解决方案1】:

,你不应该。

假设您维护一个观察者列表,在销毁时您需要取消订阅观察者对其正在观察的对象的订阅,但这需要观察者维护其观察到的实体的列表,从而导致一个循环。

为了简化设计,引入一个间接级别会更简单。观察者和实体之间的代理将允许观察者被销毁并且实体查询其活跃度。

怎么做?只需分配一个std::shared_ptr<Observer> 并让一个实体只保留一个std::weak_ptr<Observer>,然后当一个实体迭代其观察者列表时,它可以剔除对自上次迭代以来死去的人的引用。

【讨论】:

    【解决方案2】:

    不要将其视为“锁定”,请记住,您所做的只是获得另一份所有权。弱指针本身不拥有任何东西,所以不能直接使用。

    弱指针的典型用途可能在于并发代码。假设您在某处有一组由共享指针拥有的对象。然后,您可以拥有另一个引用这些相同对象的弱指针集合(例如,计时器集合)。第二个线程可能会获取弱指针,尝试从中创建共享指针,并在成功时调用对象上的计时器例程。如果任务当时已经死亡,那么共享指针将为空,第二个线程跳过该任务。

    关于std::shared_ptr 的关键事实是它的内部引用计数是自动更新的,因此您可以按照我描述的方式编写代码,并且不会出现竞争。

    Thread 1                          Thread 2
    --------                          --------
    std::set<std::shared_ptr<Task>>   std::list<std::weak_ptr<Task>> todo;
    add
    remove                            for (auto wp : todo)
                                          if (auto p = wp.lock())
                                              p->do_work();
    

    【讨论】:

    • “首先,你没有“锁定”任何东西”——成员函数被称为lock(),所以你需要向委员会提出这个问题:-)
    • @SteveJessop:如果你能写if (std::shared_ptr&lt;Foo&gt; p(my_weak_ptr)) { p-&gt;use(); },你为什么要这么做?
    • @SteveJessop:嗯,也许写if (auto p = my_weak_ptr.lock()) 会更好。很公平。
    • 你也必须向委员会提出这个问题;-p 说真的,我认为“锁定”弱指针的概念是一个有用的比喻,但我也同意你的看法, shared_ptrweak_ptr 构造函数使得 lock() 函数不是绝对必要的。我想有些人更喜欢命名转换函数而不是单参数构造函数?而您的auto 示例当然是一种很好的编写方式。
    • @KerrekSB,如果你不调用 lock 并直接构造一个 shared_ptr 它将抛出而不是构造一个空的 shared_ptr,lock() 在你不想处理异常时很有用。
    【解决方案3】:

    如果您使用weak_ptr 只是为了打破循环,那么是的,从中获取shared_ptr 应该总是成功的。

    但这不是唯一的用途。 Matthieu M. 举了一个使用它们的例子。

    您提到缓存,这对于没有循环的weak_ptr 来说是一个很好的用例,我根本不认为这是滥用。如果您有一个系统的其他部分(另一个线程或缓存)拥有的对象,并且您希望存储对该对象的引用以供以后使用,如果您使用 shared_ptr 来引用该对象,那么您共享它的所有权并将延长其寿命。如果您不确定以后是否需要该对象,因此不想延长其生命周期,您可以使用 weak_ptr 并在需要时检查它是否仍然存在。如果它不存在,您可以找到不同的对象或再次创建它(这可能会很慢,例如需要数据库查询,这就是为什么您希望尽可能重用现有对象的原因。)

    【讨论】:

    • 嘿,我正在写一个与此非常相似的答案。我接着说,通常,对于缓存,您更喜欢软引用而不是弱引用。但有时你可以主动扔掉东西,有时你想尽快删除缓存,而且无论如何 C++ 不提供软引用,所以你需要弄乱新的处理程序。
    • 我对我的描述不是很满意,因此请随时进行任何编辑以改进答案或使示例不那么抽象。
    • 谢谢!是的,Kerrek SB 和 Matthieu M 给出的示例类似于缓存,但我同意没有必要将它们视为滥用。
    【解决方案4】:

    weak_ptr 无效的用例似乎是一种竞争条件/糟糕的设计。

    weak_ptr 应该用于访问具有严格生命周期的对象,访问对象不拥有所有权

    如果你有一个非严格生命周期的对象,你应该使用shared_ptr

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-11
      • 2017-11-11
      • 2010-10-04
      • 2013-04-10
      • 1970-01-01
      • 2020-10-09
      • 1970-01-01
      • 2010-11-18
      相关资源
      最近更新 更多