Ich habe noch nicht den folgenden Weg gefunden, zyklische Referenzen zu brechen, die in irgendeinem großen C++ Forum/Blog erklärt werden, wie auf GotW, also wollte ich fragen, ob die Technik bekannt ist, und was sind ihre Vor- und Nachteile?Breaking zyklische Referenzen mit std :: weak_ptr und Alias-Konstruktor: Sound oder problematisch?
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;
};
Von außen bemerkt der Benutzer von Node
nicht den Trick mit den Aliasing-Konstruktor in getLeft
mit und getRight
, aber dennoch kann der Benutzer sicher sein, dass getParent
immer einen nicht leeren freigegebenen Zeiger zurückgibt, weil alle Zeiger, die von p->get{Left,Right}
zurückgegeben werden, behalten das Objekt *p
für die Lebensdauer des zurückgegebenen untergeordneten Zeigers am Leben.
Werde ich hier etwas übersehen, oder ist dies ein offensichtlicher Weg, zyklische Referenzen zu brechen, die bereits dokumentiert wurden?
int main() {
auto n = std::make_shared<Node>();
auto c = n->getOrCreateLeft();
// c->getParent will always return non-null even if n is reset()!
}
Bedeutet dies, dass letztendlich * alle Kinder * die gleiche Referenzzahl wie die Wurzel haben? Das heißt, wenn ich 'left_A' mit dem Aliasing-Konstruktor erstelle, ist der Referenzzähler derselbe wie 'Eltern'. Wenn ich dann ein neues 'left_B' off' left_A' erzeuge und 'left_A-> shared_from_this' verwende - ist die Referenzzählung immer noch' parent', da in der Indirektion der Baum weiterläuft? In diesem Fall teilen sich alle Knoten die gleiche Referenzanzahl und Sie können niemals einen Knoten entfernen und seine Ressourcen recyceln, bis die gesamte Struktur entfernt wurde. –
@SteveLorimer teilen sie die Referenzzahl der Wurzel nicht, es sei denn, sie wurden der Außenwelt durch eine der "Get" -Funktionen eines Knotens gegeben (die wiederum von einem anderen Knoten vergeben worden wären, und so weiter, beginnend bei der Ursprung). Wenn also nicht jemand einen Verweis auf einen Kindknoten hat (was vielleicht nur temporär der Fall ist, wenn der Baum während einer asynchronen Operation läuft), wird die Referenzzählung nicht geteilt und Ressourcen können freigegeben werden. –