Прерывание циклических ссылок с помощью std::weak_ptr и псевдонима конструктора: звучит или проблематично?
Я еще не нашел следующий способ разрыва циклических ссылок, описанный на каком-либо крупном форуме / блоге 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
не заметит хитрости с использованием конструктора псевдонимов в getLeft
а также 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()!
}
1 ответ
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
s, который владеет узлом, а не его родителем, но я полагаю, что вам будет сложнее испортить приватное объявление об использовании и запретить его по контракту.)