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

时间:2016-05-31 17:01:46

标签: 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;
};

从外部看,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()!
}

1 个答案:

答案 0 :(得分:3)

shared_ptr<Node>返回的getParent拥有父母,而不是父母的父母。

因此,在getParent上再次调用shared_ptr可以返回空(和空)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,但我认为你可以通过私人使用声明使其变得更加困难并通过合同禁止它。)