geteilte_ptr und schwache_ptr Unterschiede

75

Ich lese Scott Meyers "Effective C ++" Buch. Es wurde erwähnt, dass es eingebaute Zeiger gibt tr1::shared_ptrund tr1::weak_ptrsich wie diese verhält, aber sie verfolgen, wie viele tr1::shared_ptrsauf 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_ptrskommen 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).

venkysmarty
quelle
" Wie wird das Problem durch schwache_ptrs gelöst? " Es wird durch ein geeignetes Design gelöst, bei dem kein Eigentumszyklus existiert.
Neugieriger

Antworten:

52

A shared_ptrumschließt einen Rohzählzeiger mit einem Referenzzählmechanismus. Daher wird für jede Instanz shared_ptrder Referenzanzahl um eins erhöht. Wenn zwei share_ptrObjekte aufeinander verweisen, werden sie niemals gelöscht, da sie niemals einen Referenzzähler von Null erhalten.

weak_ptrzeigt auf a shared_ptr, erhöht jedoch nicht die Referenzanzahl. Dies bedeutet, dass das zugrunde liegende Objekt weiterhin gelöscht werden kann, obwohl ein weak_ptrVerweis darauf vorhanden ist.

Dies funktioniert so, dass Sie damit weak_ptrein shared_ptrfor 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 a shared_ptrzurückgegeben. Da die Referenzanzahl für das zugrunde liegende Objekt nicht mit einer weak_ptrReferenz erhöht wird , führt eine Zirkelreferenz nicht dazu, dass das zugrunde liegende Objekt nicht gelöscht wird.

Doron
quelle
10
Ich glaube, dass das Referenzsteuerungsobjekt sowohl "Verwendungen" (shared_ptrs) als auch "Weaks" (schwache_ptrs + (Verwendungen> 0? 1: 0)) zählt. Dies könnte jedoch ein Implementierungsdetail sein.
Ben L
119

Lassen Sie mich Ihre Frage wiederholen: "Meine Frage, wie zyklische Datenstrukturen die Referenz über Null zählen lassen, weak_ptrsbitte 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 ist shared_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_ptrwerden, 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). .

Frank
quelle
Aber alle Verwendungen von B::amüssen jetzt die schwache Referenz vorbereitet werden, um tot zu sein. Wenn dies nicht der Fall sein darf, weak_ptrist dies kein angemessenes Werkzeug.
Neugieriger
3
Wenn B :: a ein schwaches_ptr ist, sollte nichts von seiner Existenz abhängen - da es kein a besitzt. A :: b ist hier der zuverlässige Typ.
rich.e
4
Ich hätte deine als beste Antwort gewählt. Aber hey .. nicht meine Wahl :) +1 übrigens
Paul
18

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;  
Newprint
quelle
Ist schwaches_ptr nur als zyklische Referenz oder als Cache?
G10guang
7

Schwache Zeiger "beobachten" nur das verwaltete Objekt; Sie "halten es nicht am Leben" oder beeinflussen seine Lebensdauer. Im Gegensatz dazu shared_ptrkann das weak_ptrObjekt, auf das verwiesen wird, weiterhin existieren , wenn das letzte Objekt den Gültigkeitsbereich weak_ptrverlässt oder verschwindet, da es die Lebensdauer des Objekts nicht beeinflusst - es hat keine Eigentumsrechte. Mit weak_ptrkann verwendet werden, um festzustellen, ob das Objekt vorhanden ist, und um ein Objekt bereitzustellen shared_ptr, mit dem darauf verwiesen werden kann.

Die Definition von weak_ptrist so konzipiert, dass sie relativ kinderleicht ist. Daher können Sie mit a nur sehr wenig direkt tun weak_ptr. Zum Beispiel können Sie es nicht dereferenzieren; weder operator*noch operator->ist definiert für a weak_ptr. Sie können damit nicht auf den Zeiger auf das Objekt zugreifen - es gibt keine get()Funktion. Es ist eine Vergleichsfunktion definiert, mit der Sie weak_ptrsin einem bestellten Container speichern können, aber das ist alles.

peterDriscoll
quelle
-6

Alle obigen Antworten sind FALSCH. weak_ptrwird NICHT verwendet, um zyklische Referenzen zu brechen, sie haben einen anderen Zweck.

Wenn alle shared_ptr(s)von make_shared()oder allocate_shared()Aufrufe erstellt wurden, werden Sie im Grunde NIE brauchen, weak_ptrwenn Sie keine andere Ressource als Speicher zum Verwalten haben. Diese Funktionen erstellen das shared_ptrReferenzzählerobjekt mit dem Objekt selbst, und gleichzeitig wird der Speicher freigegeben.

Der einzige Unterschied zwischen weak_ptrund shared_ptrbesteht darin, weak_ptrdass das Referenzzählerobjekt beibehalten werden kann, nachdem das eigentliche Objekt freigegeben wurde. Wenn Sie also viel shared_ptrin einem std::setObjekt aufbewahren, belegen die tatsächlichen Objekte viel Speicher, wenn sie groß genug sind. Dieses Problem kann stattdessen gelöst werden weak_ptr. In diesem Fall müssen Sie sicherstellen, dass das weak_ptrim Container gespeicherte nicht abgelaufen ist, bevor Sie es verwenden.

Earth Engine
quelle
" schwach_ptr wird NICHT verwendet, um zyklische Referenzen zu brechen " +1 " sie haben einen anderen Zweck. ", aber diese Antwort erklärt nicht den Veröffentlichungszweck, also -1
neugieriger Kerl
" Wenn alle shared_ptr (s) von make_shared () erstellt wurden ", brauchen Sie nicht shared_ptr( unique_ptrist angemessener). Der Wert der Referenzzählung ist, wenn mehrere Objekte Referenzen enthalten. Dies geschieht durch Aufgaben, wie im sehr guten Beispiel.
JWM