Meine Klasse erbt von mehreren Basen, von denen eine ist std::enable_shared_from_this
. Muss es die erste Basis sein?
Angenommen, der folgende Beispielcode:
struct A { ~A(); };
struct B { ~B(); };
struct C : A, B, std::enable_shared_from_this<C> {};
std::make_shared<C>();
Wenn ~A()
und ~B()
laufe, kann ich sicher sein , dass der Speicher , in dem C
noch vorhanden gelebt ist?
std::enable_shared_from_this
macht nicht viel. Ihr Beispiel sieht nicht gut aus für mich (vorausgesetzt , Sie nicht versuchen , etwas zu tun klug in~A
und~B
, wie unten Gussthis
zuC*
)enable_shared_from_this
muss eine zugängliche, eindeutige Basis sein. In meinem Beispiel ist es.C
ist eine Struktur. Es erbt öffentlich.class
undpublic
. Ich habe michstruct
für das Beispiel entschieden, um das Tippen zu vermeiden.~weak_ptr();
Effekte: Zerstört diesesweak_ptr
Objekt, hat jedoch keine Auswirkungen auf das Objekt, auf das sein gespeicherter Zeiger zeigt." Hervorhebung von mir.shared_ptr
Tod. Selbst wenn das verhindert, dassweak_ptr
der Kontrollblock freigegeben wird, denke ich nicht, dass es wichtig ist.Antworten:
Na sicher! Es wäre schwierig, eine Basisklasse zu verwenden, die versucht, ihren eigenen Speicher (den Speicher, in dem sie sich befindet) freizugeben. Ich bin mir nicht sicher, ob es überhaupt formal legal ist.
Implementierungen tun dies nicht: Wenn a
shared_ptr<T>
zerstört oder zurückgesetzt wird, wird der Referenzzähler (RC) für den gemeinsamen Besitz vonT
(atomar) dekrementiert; Wenn es im Dekrement 0 erreicht hat, wird die Zerstörung / Löschung vonT
gestartet.Dann wird die Anzahl der schwachen Eigentümer oder T-existierenden (atomar) dekrementiert, da sie
T
nicht mehr existiert: Wir müssen wissen, ob wir die letzte Entität sind, die an dem Kontrollblock interessiert ist; Wenn die Dekrementierung ein Ergebnis ungleich Null ergab, bedeutet diesweak_ptr
, dass einige vorhanden sind (möglicherweise 1 Anteil oder 100%), die Eigentümer des Kontrollblocks sind, und sie sind jetzt für die Freigabe verantwortlich.In beiden Fällen wird die atomare Dekrementierung für den letzten Miteigentümer irgendwann zu einem Wert von Null führen.
Hier gibt es keine Fäden, keinen Nichtdeterminismus, und offensichtlich wurde der letzte
weak_ptr<T>
während der Zerstörung von zerstörtC
. (Die ungeschriebene Annahme in Ihrer Frage ist, dass kein andererweak_ptr<T>
beibehalten wurde.)Zerstörung geschieht immer in genau dieser Reihenfolge. Der Steuerblock wird zur Zerstörung verwendet, da
shared_ptr<T>
(im Allgemeinen) nicht bekannt ist, welcher (möglicherweise nicht virtuelle) Destruktor der (möglicherweise unterschiedlichen) am meisten abgeleiteten Klasse aufgerufen werden soll . (Der Steuerblock weiß auch, dass der Speicher nicht freigegeben werden soll, wenn die gemeinsame Anzahl Null erreichtmake_shared
.)Die einzige praktische Variation zwischen den Implementierungen scheint in den feinen Details von Speicherzäunen und der Vermeidung einiger atomarer Operationen in häufigen Fällen zu bestehen.
quelle
weak_count
1 für ein Objekt, dasmake_shared
-ed wurde, auch wenn es keinweak_ptr
s gibt. Nur dieshared_ptr
ersten Dekremente freigebenuse_count
. Wenn es 0 wird, wird das Objekt (aber nicht der Steuerblock) zerstört. Dannweak_count
wird dekrementiert und wenn 0, wird der Steuerblock zerstört + freigegeben. Ein Objekt, das von erbt,enable_shared_from_this
beginnt mitweak_count
= 2. Eine brillante Lösung von STL-Implementierern, wie erwartet.random_access_iterator_tag
. B. ). Es besteht eine informelle Vereinbarung, alles, was mit Containern zu tun hat, als Teil der STL zu bezeichnen. tl; dr: Nicht alle Vorlagen in der Standardbibliothek sind Teil der STL und nicht alle Nicht-Vorlagen befinden sich außerhalb der STL.Nein, und die Reihenfolge der Basisklassen ist irrelevant. Sogar die Verwendung (oder nicht) von enable_shared_from_this ist irrelevant.
Wenn ein C-Objekt zerstört wird (was auch immer passiert),
~C()
wird es vor beiden aufgerufen,~A()
und~B()
so funktionieren Basis-Destruktoren. Wenn Sie versuchen, das C-Objekt in einem der Basisdestruktoren und Zugriffsfelder darin zu "rekonstruieren", wurden diese Felder bereits zerstört, sodass Sie ein undefiniertes Verhalten erhalten.quelle
enable_shared_from_this
Kann an einer beliebigen Stelle in der Basisliste erscheinen. Implementierungen sind erforderlich, um nach der Zerstörung des gesamten Objekts Speicher freizugeben, unabhängig davon, wie es vonenable_shared_from_this
" oder "It" erbt muss die erste Basis sein, erbt irgendwo anders UB "oder" Dieses Verhalten ist nicht spezifiziert oder die Qualität der Implementierung ".Wenn Sie ein Objekt c vom Typ C mit den Basen A, B und einem Referenzzähler durch Erben von der Basis erstellen
enable_shared_from_this<T>
, wird zunächst Speicher für das gesamte resultierende Objekt zugewiesen, einschließlich der Basen im Allgemeinen und der Basisenable_shared_from_this<T>
. Das Objekt wird erst zerstört, wenn der letzte Eigentümer (auch bekannt als shared_ptr) das Eigentum aufgibt. In diesem Moment werden ~ enable_shared ..., ~ B und ~ A nach ~ C ausgeführt. Der vollständig zugewiesene Speicher ist garantiert noch vorhanden, bis der letzte Destruktor ~ A ausgeführt wird. Nachdem ~ A ausgeführt wurde, wird der gesamte Objektspeicher auf einen Schlag freigegeben. Um Ihre Frage zu beantworten:Ja, obwohl Sie nicht legal darauf zugreifen können, aber warum sollten Sie es wissen müssen? Welches Problem versuchen Sie zu vermeiden?
quelle
shared_ptr
,weak_ptr
undenable_shared_from_this
erforderlich sind, um den Speicher lange genug zu halten, um dies sicher zu machen, auch wenn diesenable_shared_from_this
nicht die erste Basis ist.enable_shared_from_this
nach einer anderen Basisklasse.