普通引用而不是 weak_ptr 来打破循环依赖

Plain reference instead of weak_ptr to break circular dependency

看看 std::weak_ptr 我在几个地方看到它可以用来打破由于使用 std::shared_ptr 循环依赖引起的内存泄漏。例如,请参见这两个已接受的答案:[1], [2].

采用最后引用的答案,建议的修复是:

#include <memory>
#include <iostream>

struct B;
struct A {
  std::shared_ptr<B> b;  
  ~A() { std::cout << "~A()\n"; }
};

struct B {
  std::weak_ptr<A> a;
  ~B() { std::cout << "~B()\n"; }  
};

void useAnB() {
  auto a = std::make_shared<A>();
  auto b = std::make_shared<B>();
  a->b = b;
  b->a = a;
}

int main() {
   useAnB();
   std::cout << "Finished using A and B\n";
}

不过,这感觉有点矫枉过正,为什么不简单地使用引用呢?我知道在这个例子中 b->a 没有在构造函数中设置,所以引用不会真正削减它,所以我的问题是:

Is there a reason to use a std::weak_ptr instead of a reference if the point is to break circular dependency if we can set it in the constructor?

注意: 我明白 std::weak_ptr 的用处是保存一个可以失效的引用。我的问题只涉及当要点专门打破循环依赖时的用处。

这里有一个稍微修改过的函数:

void useAnB() {
  std::shared_ptr<B> oops;
  {
      auto a = std::make_shared<A>();
      auto b = std::make_shared<B>();
      a->b = b;
      b->a = a;
      oops = b;
  }
  // use oops->a 
}

如果 oops->a 是普通指针或引用,您怎么知道它不再引用有效对象?

您无法检查引用(或普通指针)是否引用现有对象,并且您不能 "reseat" 引用,例如在赋值中。

I understand the usefulness of std::weak_ptr to hold a reference that can be invalidated. My question only concerns the usefulness when the point is exclusively to break circular dependency.

好吧,如果你有循环依赖(或者即使你没有)并且你想在其他地方存储对 'master' 对象的引用以便你可以访问它,你知道确定那个对象不会消失,而你可能想要这样做,那么是的,我会只使用一个引用,因为:

  • 更便宜
  • 它可能避免首先将主对象设为 shared_ptr(而这些本身非常昂贵)

归根结底,就是设计您的对象以最好地执行您需要它们完成的任何工作。当然,std::weak_ptr 并不是每个少女祈祷的答案。