5

我还没有在任何主要的 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;
};

从外部看,用户Node不会注意到在getLeftand中使用别名构造函数的技巧getRight,但用户仍然可以确定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()!
}
4

1 回答 1

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

(继承的shared_from_this也传递出shared_ptr拥有节点而不是其父节点的 s,但我想你可以通过私有 using 声明更难搞砸并通过合同禁止它。)

于 2016-05-31T17:24:20.830 回答