使用std :: weak_ptr和别名构造函数打破循环引用:声音还是有问题的?

Joh*_*itb 5 c++ shared-ptr weak-ptr cyclic-reference

我还没有找到以下方法来破解在任何主要的C++论坛/博客上解释的循环引用,比如GotW,所以我想问一下这种技术是否已知,它的优缺点是什么?

class Node : public std::enable_shared_from_this<Node> {
public:
   std::shared_ptr<Node> getParent() {
      return parent.lock();    
   }

   // the getter functions ensure that "parent" always stays alive!
   std::shared_ptr<Node> getLeft() {
       return std::shared_ptr<Node>(shared_from_this(), left.get());
   }

   std::shared_ptr<Node> getRight() {
       return std::shared_ptr<Node>(shared_from_this(), right.get());
   }

   // add children.. never let them out except by the getter functions!
public:
   std::shared_ptr<Node> getOrCreateLeft() {
       if(auto p = getLeft())
          return p;
       left = std::make_shared<Node>();
       left->parent = shared_from_this();
       return getLeft();
   }

   std::shared_ptr<Node> getOrCreateRight() {
       if(auto p = getRight())
          return p;
       right = std::make_shared<Node>();
       right->parent = shared_from_this();
       return getRight();
   }

private:
   std::weak_ptr<Node> parent;
   std::shared_ptr<Node> left;
   std::shared_ptr<Node> right;
};
Run Code Online (Sandbox Code Playgroud)

从外面看,用户Node将无法使用混叠构造中发现的伎俩getLeftgetRight,但仍是用户可以肯定的是getParent总是返回一个非空的共享指针,因为返回的所有指针p->get{Left,Right}保持对象*p存活的生命周期返回的子指针.

我是否忽略了这里的某些内容,或者这是否已经成为打破已经记录的循环引用的明显方法?

int main() {
   auto n = std::make_shared<Node>();
   auto c = n->getOrCreateLeft();
   // c->getParent will always return non-null even if n is reset()!
}
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 3

您返回shared_ptr<Node>getParent父级,而不是父级的父级。

因此,getParent再次调用它shared_ptr可以返回空(和 null)shared_ptr。例如:

int main() {
   auto gp = std::make_shared<Node>();
   auto p = gp->getOrCreateLeft();
   auto c = p->getOrCreateLeft();
   gp.reset();
   p.reset(); // grandparent is dead at this point
   assert(c->getParent());
   assert(!c->getParent()->getParent());
}
Run Code Online (Sandbox Code Playgroud)

(继承的shared_from_this还会传递出shared_ptr拥有该节点而不是其父节点的 s,但我想您可以通过私有 using 声明来使混乱变得更加困难,并通过合同禁止它。)