Ich lese Scott Meyers "Effective C ++" Buch. Es wurde erwähnt, dass es eingebaute Zeiger gibt tr1::shared_ptr
und tr1::weak_ptr
sich wie diese verhält, aber sie verfolgen, wie viele tr1::shared_ptrs
auf ein Objekt zeigen.
Dies wird als Referenzzählung bezeichnet. Dies funktioniert gut, um Ressourcenlecks in azyklischen Datenstrukturen zu verhindern. Wenn jedoch zwei oder mehr Objekte so enthalten tr1::shared_ptrs
, dass ein Zyklus gebildet wird, kann der Zyklus den Referenzzählwert des jeweils anderen über Null halten, selbst wenn alle externen Zeiger auf den Zyklus zerstört wurden.
Hier tr1::weak_ptrs
kommen Sie rein.
Meine Frage ist, wie zyklische Datenstrukturen den Referenzzähler über Null bringen. Ich bitte um ein Beispiel für ein C ++ - Programm. Wie wird das Problem gelöst weak_ptrs
? (wieder mit Beispiel bitte).
quelle
Antworten:
A
shared_ptr
umschließt einen Rohzählzeiger mit einem Referenzzählmechanismus. Daher wird für jede Instanzshared_ptr
der Referenzanzahl um eins erhöht. Wenn zweishare_ptr
Objekte aufeinander verweisen, werden sie niemals gelöscht, da sie niemals einen Referenzzähler von Null erhalten.weak_ptr
zeigt auf ashared_ptr
, erhöht jedoch nicht die Referenzanzahl. Dies bedeutet, dass das zugrunde liegende Objekt weiterhin gelöscht werden kann, obwohl einweak_ptr
Verweis darauf vorhanden ist.Dies funktioniert so, dass Sie damit
weak_ptr
einshared_ptr
for erstellen können, wann immer Sie das zugrunde liegende Objekt verwenden möchten. Wenn das Objekt jedoch bereits gelöscht wurde, wird eine leere Instanz von ashared_ptr
zurückgegeben. Da die Referenzanzahl für das zugrunde liegende Objekt nicht mit einerweak_ptr
Referenz erhöht wird , führt eine Zirkelreferenz nicht dazu, dass das zugrunde liegende Objekt nicht gelöscht wird.quelle
Lassen Sie mich Ihre Frage wiederholen: "Meine Frage, wie zyklische Datenstrukturen die Referenz über Null zählen lassen,
weak_ptrs
bitte um Vorlage mit Beispiel im C ++ - Programm. Wie das Problem durch erneutes Beispiel bitte gelöst wird ."Das Problem tritt bei C ++ - Code wie folgt auf (konzeptionell):
class A { shared_ptr<B> b; ... }; class B { shared_ptr<A> a; ... }; shared_ptr<A> x(new A); // +1 x->b = new B; // +1 x->b->a = x; // +1 // Ref count of 'x' is 2. // Ref count of 'x->b' is 1. // When 'x' leaves the scope, there will be a memory leak: // 2 is decremented to 1, and so both ref counts will be 1. // (Memory is deallocated only when ref count drops to 0)
Um den zweiten Teil Ihrer Frage zu beantworten: Es ist mathematisch unmöglich, mit Referenzzählungen Zyklen zu behandeln. Daher kann a
weak_ptr
(was im Grunde nur eine abgespeckte Version von istshared_ptr
) nicht zur Lösung des Zyklusproblems verwendet werden - der Programmierer löst das Zyklusproblem.Um dies zu lösen, muss der Programmierer die Eigentumsbeziehung zwischen den Objekten kennen oder eine Eigentumsbeziehung erfinden, wenn ein solches Eigentum auf natürliche Weise nicht besteht.
Der obige C ++ - Code kann so geändert werden, dass A B besitzt:
class A { shared_ptr<B> b; ... }; class B { weak_ptr<A> a; ... }; shared_ptr<A> x(new A); // +1 x->b = new B; // +1 x->b->a = x; // No +1 here // Ref count of 'x' is 1. // Ref count of 'x->b' is 1. // When 'x' leaves the scope, its ref count will drop to 0. // While destroying it, ref count of 'x->b' will drop to 0. // So both A and B will be deallocated.
Eine entscheidende Frage ist: Kann verwendet
weak_ptr
werden, wenn der Programmierer die Eigentumsbeziehung nicht mitteilen und aufgrund fehlender Berechtigungen oder Informationen keine statische Eigentümerschaft herstellen kann?Die Antwort lautet: Wenn der Besitz von Objekten unklar ist,
weak_ptr
kann dies nicht helfen. Wenn es einen Zyklus gibt, muss der Programmierer ihn finden und unterbrechen. Ein alternatives Mittel ist die Verwendung einer Programmiersprache mit vollständiger Speicherbereinigung (z. B. Java, C #, Go, Haskell) oder eines konservativen (= unvollständigen) Speicherbereinigungsprogramms, das mit C / C ++ funktioniert (z. B. Boehm GC). .quelle
B::a
müssen jetzt die schwache Referenz vorbereitet werden, um tot zu sein. Wenn dies nicht der Fall sein darf,weak_ptr
ist dies kein angemessenes Werkzeug.Für zukünftige Leser.
Ich möchte nur darauf hinweisen, dass die Erklärung von Atom ausgezeichnet ist. Hier ist Arbeitscode
#include <memory> // and others using namespace std; class B; // forward declaration // for clarity, add explicit destructor to see that they are not called class A { public: shared_ptr<B> b; ~A() {cout << "~A()" << endl; } }; class B { public: shared_ptr<A> a; ~B() {cout << "~B()" << endl; } }; shared_ptr<A> x(new A); //x->b share_ptr is default initialized x->b = make_shared<B>(); // you can't do "= new B" on shared_ptr x->b->a = x; cout << x.use_count() << endl;
quelle
Schwache Zeiger "beobachten" nur das verwaltete Objekt; Sie "halten es nicht am Leben" oder beeinflussen seine Lebensdauer. Im Gegensatz dazu
shared_ptr
kann dasweak_ptr
Objekt, auf das verwiesen wird, weiterhin existieren , wenn das letzte Objekt den Gültigkeitsbereichweak_ptr
verlässt oder verschwindet, da es die Lebensdauer des Objekts nicht beeinflusst - es hat keine Eigentumsrechte. Mitweak_ptr
kann verwendet werden, um festzustellen, ob das Objekt vorhanden ist, und um ein Objekt bereitzustellenshared_ptr
, mit dem darauf verwiesen werden kann.Die Definition von
weak_ptr
ist so konzipiert, dass sie relativ kinderleicht ist. Daher können Sie mit a nur sehr wenig direkt tunweak_ptr
. Zum Beispiel können Sie es nicht dereferenzieren; wederoperator*
nochoperator->
ist definiert für aweak_ptr
. Sie können damit nicht auf den Zeiger auf das Objekt zugreifen - es gibt keineget()
Funktion. Es ist eine Vergleichsfunktion definiert, mit der Sieweak_ptrs
in einem bestellten Container speichern können, aber das ist alles.quelle
Alle obigen Antworten sind FALSCH.
weak_ptr
wird NICHT verwendet, um zyklische Referenzen zu brechen, sie haben einen anderen Zweck.Wenn alle
shared_ptr(s)
vonmake_shared()
oderallocate_shared()
Aufrufe erstellt wurden, werden Sie im Grunde NIE brauchen,weak_ptr
wenn Sie keine andere Ressource als Speicher zum Verwalten haben. Diese Funktionen erstellen dasshared_ptr
Referenzzählerobjekt mit dem Objekt selbst, und gleichzeitig wird der Speicher freigegeben.Der einzige Unterschied zwischen
weak_ptr
undshared_ptr
besteht darin,weak_ptr
dass das Referenzzählerobjekt beibehalten werden kann, nachdem das eigentliche Objekt freigegeben wurde. Wenn Sie also vielshared_ptr
in einemstd::set
Objekt aufbewahren, belegen die tatsächlichen Objekte viel Speicher, wenn sie groß genug sind. Dieses Problem kann stattdessen gelöst werdenweak_ptr
. In diesem Fall müssen Sie sicherstellen, dass dasweak_ptr
im Container gespeicherte nicht abgelaufen ist, bevor Sie es verwenden.quelle
shared_ptr
(unique_ptr
ist angemessener). Der Wert der Referenzzählung ist, wenn mehrere Objekte Referenzen enthalten. Dies geschieht durch Aufgaben, wie im sehr guten Beispiel.