Herr Lidström und ich hatten einen Streit :)
Die Behauptung von Herrn Lidström ist, dass für ein Konstrukt shared_ptr<Base> p(new Derived);
Base keinen virtuellen Destruktor benötigt:
Armen Tsirunyan : "Wirklich? Wird der shared_ptr korrekt bereinigt? Könnten Sie bitte in diesem Fall zeigen, 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 als RAII bezeichnet. Mein Rat ist, dass Sie alles über RAII lernen. Dies erleichtert Ihre C ++ - Codierung bei der Verwendung erheblich RAII in allen Situationen. "
Armen Tsirunyan : "Ich weiß über RAII Bescheid, und ich weiß auch, dass der Destruktor shared_ptr möglicherweise den gespeicherten px löscht, wenn pn 0 erreicht. Wenn px jedoch einen statischen Typzeiger auf
Base
und einen dynamischen Typzeiger auf hatDerived
, dann, sofernBase
kein virtueller Destruktor vorhanden ist führt zu undefiniertem Verhalten. Korrigieren Sie mich, wenn ich falsch liege. "Daniel Lidström : "Der shared_ptr weiß, dass der statische Typ Concrete ist. Er weiß das, seit ich ihn in seinem Konstruktor übergeben habe! Scheint ein bisschen magisch, aber ich kann Ihnen versichern, dass er beabsichtigt und äußerst nett ist."
Also, beurteilen Sie uns. Wie ist es möglich (wenn ja ), shared_ptr zu implementieren, ohne dass polymorphe Klassen einen virtuellen Destruktor haben müssen? Danke im Voraus
quelle
shared_ptr<void> p(new Derived)
dasDerived
Objekt auch durch seinen Destruktor zerstört wird, unabhängig davon, ob es istvirtual
oder nicht.shared_ptr<T>( (T*)new U() )
wostruct U:T
wird nicht das Richtige getan (und dies kann indirekt leicht getan werden, wie eine Funktion, die a übernimmtT*
und a übergeben wirdU*
)Antworten:
Ja, es ist möglich, shared_ptr auf diese Weise zu implementieren. Boost funktioniert und der C ++ 11-Standard erfordert dieses Verhalten ebenfalls. Als zusätzliche Flexibilität verwaltet shared_ptr mehr als nur einen Referenzzähler. Ein sogenannter Deleter wird normalerweise in denselben Speicherblock gestellt, der auch die Referenzzähler enthält. Der lustige Teil ist jedoch, dass der Typ dieses Löschers nicht Teil des Typs shared_ptr ist. Dies wird als "Typlöschung" bezeichnet und ist im Grunde die gleiche Technik, die zum Implementieren der Boost :: -Funktion "polymorpher Funktionen" oder der std :: -Funktion zum Ausblenden des tatsächlichen Funktortyps verwendet wird. Damit Ihr Beispiel funktioniert, benötigen wir einen Konstruktor mit Vorlagen:
Wenn Sie dies also mit Ihren Klassen Base und Derived verwenden ...
... wird der Vorlagenkonstruktor mit Y = Derived verwendet, um das shared_ptr-Objekt zu erstellen. 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 zum Entsorgen des Objekts verwendet.
Der C ++ 11-Standard hat Folgendes zu diesem Konstruktor (20.7.2.2.1) zu sagen:
Und für den Destruktor (20.7.2.2.2):
(Die Betonung mit Fettschrift liegt bei mir).
quelle
the upcoming standard also requires this behaviour
: (a) Welcher Standard und (b) können Sie bitte einen Verweis (auf den Standard) geben?add a comment
. IMO, es ist mehrBoost does this
alsthe Standard requires
. Ich glaube nicht, dass der Standard dies nach meinem Verständnis erfordert. Talking about @sellibitze ‚s Beispielshared_ptr<Base> sp (new Derived);
, erfordert vonconstructor
fragen nur fürdelete Derived
sein gut definiert und gut ausgebildet. Für die Spezifikation vondestructor
gibt es auch einep
, aber ich denke nicht, dass sie sich auf diep
in der Spezifikation von beziehtconstructor
.Wenn shared_ptr erstellt wird, speichert es ein Löscherobjekt in sich. Dieses Objekt wird aufgerufen, wenn shared_ptr die freigegebene Ressource freigeben soll. Da Sie wissen, wie Sie die Ressource zum Zeitpunkt der Erstellung zerstören, können Sie shared_ptr mit unvollständigen Typen verwenden. Wer auch immer den shared_ptr erstellt hat, hat dort einen korrekten Löscher gespeichert.
Sie können beispielsweise einen benutzerdefinierten Löscher erstellen:
p ruft DeleteDerived auf, um das spitze Objekt zu zerstören. Die Implementierung erledigt dies automatisch.
quelle
shared_ptr
als Attribut.Einfach,
shared_ptr
verwendet eine spezielle Löschfunktion, die vom Konstruktor erstellt wird, der immer den Destruktor des angegebenen Objekts und nicht den Destruktor von Base verwendet. Dies ist ein wenig Arbeit mit der Vorlagen-Metaprogrammierung, funktioniert aber.Sowas in der Art
quelle