shared_ptr中weak_ptr的作用

4

我知道shared_ptr是如何工作的,但weak_ptr的作用还不太清楚。我知道它可以在引用计数不为零时检测循环引用,但除此之外,我不太明白它是如何做到这一点的。它具体是做什么呢?


可能是shared_ptr和weak_ptr的区别的重复问题。 - Peter Clark
3
不,弱指针很少用于解决循环引用的问题。它们在其他方面更加有用。 - Kerrek SB
1
@PeterClark 我无法理解那个问题的答案,关于weak_ptrs的部分不是很连贯。 - user997112
1
通常情况下,shared_ptr 适合在标准容器中存储一些共享信息,而在函数调用中使用 weak_ptr 传递参数。 - Tanuki
@user997112 抱歉,没有看到你对我的回复。我认为一般的做法是如果你要评论答案,就在答案上发表评论(不确定如果你在问题本身上发表评论是否会通知我,而且我还没有在问题上发表评论)。有什么特别困惑的地方吗?需要澄清的一件事是 - 你是在寻找 weak_ptr 的工作原理,还是在寻找为什么要使用它?我回答了第二个问题(这是你的标题所暗示的),但是你的问题正文似乎在寻找第一个问题的答案。 - Peter Clark
@Tanuki “在函数调用中使用weak_ptr来传递参数” 你为什么要这么做?!! - curiousguy
2个回答

3
另请参阅:何时使用std::weak_ptr以了解原因,weak_ptr如何工作以了解实现方式。
我将提供一个示例,说明我见过的用法,尽管我写的示例代码有些复杂,请耐心等待:
#include <vector>
#include <memory>
#include <ostream>

int main()
{
  // Fill container with values 1-50. This container OWNS these values.
  std::vector<std::shared_ptr<int>> owning_container;
  for(int i = 1; i <= 50; ++i)
  {
    owning_container.emplace_back(std::make_shared<int>(i));
  }

  // Create a sepearate container that references all owned values that are multiples of 5.
  std::vector<std::weak_ptr<int>> referencing_container;
  for(std::shared_ptr<int> const& i : owning_container)
  {
    if((*i) % 5 == 0)
    {
      // Make weak_ptr that references shared_ptr
      referencing_container.emplace_back(i);
    }
  }

  // Go through the owned values and delete all that are multiples of 10.
  for(auto it = owning_container.begin(); it != owning_container.end();)
  {
    std::shared_ptr<int> const& i = *it;
    if(*i % 10 == 0)
    {
      it = owning_container.erase(it);
    }
    else
    {
      ++it;
    }
  }

  // Now go through the referencing container and print out all values.
  // If we were dealing with raw pointers in both containers here we would access deleted memory,
  //   since some of the the actual resources (in owning_container) that referencing_container
  //   references have been removed.
  for(std::weak_ptr<int> const& i_ref : referencing_container)
  {
    // Check if the shared resource still exists before using it (purpose of weak_ptr)
    std::shared_ptr<int> i = i_ref.lock();
    if(i)
    {
      std::cout << *i << std::endl;
    }
  }

  return 0;
}

这里有一个包含一些共享资源的容器,这些资源是int类型的(shared_ptr),另一个容器需要引用它们 (weak_ptr)。引用并不拥有这个资源,只需要在需要时访问它(如果存在)。为了检查被引用的资源是否仍然存在,我们使用weak_ptr::lock()weak_ptr转换为shared_ptr。那些仍然存在的资源将会返回一个有效的shared_ptr,而那些不存在的资源将返回一个空的shared_ptr。我们可以使用shared_ptroperator bool()来检查它是否为null,然后再使用它。

一个更简单的场景可能是,假设你正在制作一个游戏,游戏中的每个对象都由一个game_object表示。比如说你有一个寻找逻辑用于向敌人寻找目标game_object。使用以上方法,你可以让敌人持有一个weak_ptr<game_object>。它并没有拥有这个game_object,如果其他东西杀死了它的目标,它的目标应该死亡,而不是停留在某种悬空状态。如果敌人的目标仍然存在的话(它可以通过锁定weak_ptr来检查),则可以执行寻找逻辑;否则,它可以寻找一个新的目标。这个game_object的"所有者"可以是某种game_world类 - 它将拥有一个shared_ptr<game_object>容器。当敌人需要一个新的目标时,它可以搜索这个容器,并从game_worldshared_ptr创建它的weak_ptr


2
弱指针不声明对资源的所有权,而只是引用它。因此,除了再次声明所有权(使用weak_ptr::lock()方法),它们不允许您以任何方式操作资源。我的意见是,现实生活中最常见的需要这种行为的情况是循环依赖和(较少)缓存。
使用仅共享指针创建的循环依赖实际上是内存泄漏,因为指针的相互引用计数永远不会小于1:如果没有其他东西拥有A,那么B仍然拥有A,反之亦然。弱指针不会“检测”这种情况。它只是通过打破所有权的循环来防止问题的发生。您仍然可以通过锁定弱指针“连接链的末端”,但是通过弱指针获得的任何共享指针都不会持久存在。
缓存的问题再次出现,一个缓存通常不应影响缓存内容的生命周期,这不是缓存的职责。但是,如果缓存会保留共享指针,那么您将无法终止缓存对象的生命周期,而无需与缓存进行交互,这通常是不方便的。

你能提供一些循环依赖案例,其中弱引用有所帮助的示例吗? - curiousguy

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