Mr. Lidström und ich hatten einen Streit :)
Herr. Lidströms Behauptung ist, dass ein Konstrukt shared_ptr<Base> p(new Derived);
nicht benötigt, dass Base einen virtuellen Destruktor hat:
Armen Tsirunyan : Wirklich? Wird shared_ptr richtig aufgeräumt? Könnten Sie bitte in diesem Fall demonstrieren, wie dieser Effekt implementiert werden könnte?
Daniel Lidström : Der shared_ptr verwendet einen eigenen Destruktor, um die Concrete-Instanz zu löschen. Dies wird in der C ++ - Community RAII genannt. Mein Rat ist, dass Sie alles lernen Sie können über RAII. Es wird Ihre C ++ - Codierung so viel einfacher machen, wenn Sie RAII in allen Situationen verwenden. "
Armen Tsirunyan : Ich kenne RAII, und ich weiß auch, dass der shared_ptr Destruktor möglicherweise den gespeicherten px löscht, wenn pn 0 erreicht. Aber wenn px statisch ist Geben Sie den Zeiger auf
Base
und den Zeiger des dynamischen Typs aufDerived
ein, wenn nichtBase
einen virtuellen Destruktor hat, führt dies zu undefiniertem Verhalten. Korrigieren Sie mich, wenn ich falsch liege. "Daniel Lidström : "Der shared_ptr kennt den statischen Typ" Concrete ". Er weiß es, seit ich es in seinem Konstruktor übergeben habe! Scheint ein bisschen wie Magie, aber ich kann versichere Ihnen, dass es von Design und extrem nett ist. "
Also, urteile über uns. Wie ist es möglich (wenn), shared_ptr zu implementieren, ohne dass polymorphe Klassen einen virtuellen Destruktor haben müssen? Vielen Dank im Voraus
Ja, es ist möglich, shared_ptr auf diese Weise zu implementieren. Boost tut das und der C ++ 11-Standard erfordert auch dieses Verhalten. Als zusätzliche Flexibilität verwaltet shared_ptr mehr als nur einen Referenzzähler. Ein sogenannter Löscher wird normalerweise in denselben Speicherblock gesetzt, der auch die Referenzzähler enthält. Der spaßige Teil ist jedoch, dass der Typ dieses Deleters nicht Teil des Typs shared_ptr ist. Dies wird als "Typ löschen" bezeichnet und ist im Grunde die gleiche Technik, die zum Implementieren der "polymorphen Funktionen" boost :: function oder std :: function zum Verbergen des tatsächlichen Funktortyps verwendet wird. Damit Ihr Beispiel funktionieren kann, benötigen wir einen Vorlagenkonstruktor:
%Vor%Also, wenn Sie dies mit Ihren Klassen Base und Derived ... verwenden
%Vor%... wird der Konstruktor mit dem Template mit Y = Derived verwendet, um das Objekt shared_ptr zu konstruieren. Der Konstruktor hat somit die Möglichkeit, die entsprechenden Löschobjekt- und Referenzzähler zu erstellen und einen Zeiger auf diesen Steuerblock als Datenelement zu speichern. Wenn der Referenzzähler null erreicht, wird der zuvor erstellte und abgeleitete Deleter verwendet, um das Objekt zu entfernen.
Der C ++ 11-Standard besagt Folgendes über diesen Konstruktor (20.7.2.2.1):
Voraussetzung:
p
muss inT*
konvertierbar sein.Y
muss ein vollständiger Typ sein. Der Ausdruckdelete p
soll wohlgeformt sein, wohldefiniertes Verhalten haben und keine Ausnahmen auslösen.Effekte: Konstruiert ein
shared_ptr
Objekt das besitzt den Zeigerp
....
Und für den Destruktor (20.7.2.2.2):
Effekte: Wenn
*this
leer ist oder das Eigentum an einer anderenshared_ptr
-Instanz (use_count() > 1
) teilt, gibt es keine Nebenwirkungen. Andernfalls, wenn*this
ein Objektp
besitzt und ein Deleterd
, wirdd(p)
aufgerufen. Andernfalls, wenn*this
einen Zeigerp
besitzt unddelete p
aufgerufen wird.
(Hervorhebung mit Fettschrift gehört mir).
Wenn shared_ptr erstellt wird, speichert es ein Deleter Objekt in sich. Dieses Objekt wird aufgerufen, wenn das shared_ptr die angegebene Ressource freigibt. Da Sie wissen, wie Sie die Ressource zum Zeitpunkt der Konstruktion zerstören können, können Sie shared_ptr mit unvollständigen Typen verwenden. Wer auch immer shared_ptr erstellt hat, hat dort einen korrekten Deleter gespeichert.
Sie können beispielsweise einen benutzerdefinierten Löschvorgang erstellen:
%Vor%p ruft DeleteDerived auf, um das spitze Objekt zu zerstören. Die Implementierung macht dies automatisch.
Einfach,
shared_ptr
verwendet eine spezielle Löschfunktion, die vom Konstruktor erstellt wird, der immer verwendet
der Destruktor des gegebenen Objekts und nicht der Destruktor von Base, das ist ein bisschen Arbeit mit Template-Meta-Programmierung, aber es funktioniert.
So ähnlich
%Vor%Tags und Links c++ smart-pointers destructor