Es ermöglicht Ihnen, eine gültige shared_ptr
Instanz zu erhalten this
, wenn alles, was Sie haben, ist this
. Ohne sie hätten Sie keine Möglichkeit, einen Zugang shared_ptr
zu erhalten this
, es sei denn, Sie hätten bereits einen als Mitglied. Dieses Beispiel aus der Boost-Dokumentation für enable_shared_from_this :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Die Methode f()
gibt eine gültige zurück shared_ptr
, obwohl sie keine Mitgliedsinstanz hatte. Beachten Sie, dass Sie dies nicht einfach tun können:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Der gemeinsam genutzte Zeiger, den dieser zurückgegeben hat, hat eine andere Referenzanzahl als der "richtige", und einer von ihnen verliert und hält eine baumelnde Referenz, wenn das Objekt gelöscht wird.
enable_shared_from_this
ist Teil des C ++ 11-Standards geworden. Sie können es auch von dort sowie von Boost bekommen.
1800 INFORMATIONEN
quelle
std::shared_ptr
Konstruktor auf einem Rohzeiger wenn es erbtstd::enable_shared_from_this
. Ich weiß nicht, ob die Semantik von Boost aktualisiert wurde, um dies zu unterstützen.std::shared_ptr
für ein Objekt, das bereits von einem anderen verwaltetstd::shared_ptr
wird, konsultiert nicht die intern gespeicherte schwache Referenz und führt daher zu undefiniertem Verhalten." ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )shared_ptr<Y> q = p
?std::make_shared<T>
.Aus dem Artikel von Dr. Dobbs über schwache Zeiger geht hervor, dass dieses Beispiel leichter zu verstehen ist (Quelle: http://drdobbs.com/cpp/184402026 ):
... Code wie dieser funktioniert nicht richtig:
Keines der beiden
shared_ptr
Objekte kennt das andere, daher versuchen beide, die Ressource freizugeben, wenn sie zerstört werden. Das führt normalerweise zu Problemen.Wenn eine Mitgliedsfunktion ein
shared_ptr
Objekt benötigt, das das Objekt besitzt, für das sie aufgerufen wird, kann sie nicht einfach ein Objekt im laufenden Betrieb erstellen:Dieser Code hat das gleiche Problem wie das vorherige Beispiel, allerdings in einer subtileren Form. Wenn es erstellt wird, besitzt das
shared_pt
r-Objektsp1
die neu zugewiesene Ressource. Der Code in der Member-FunktionS::dangerous
kennt diesesshared_ptr
Objekt nicht, daher unterscheidet sich das zurückgegebeneshared_ptr
Objekt vonsp1
. Das Kopieren des neuenshared_ptr
Objekts nachsp2
hilft nicht. Wennsp2
der Gültigkeitsbereich verlassen wird, wird die Ressource freigegeben, und wennsp1
der Gültigkeitsbereich verlassen wird, wird die Ressource erneut freigegeben.Um dieses Problem zu vermeiden, verwenden Sie die Klassenvorlage
enable_shared_from_this
. Die Vorlage verwendet ein Vorlagentypargument, bei dem es sich um den Namen der Klasse handelt, die die verwaltete Ressource definiert. Diese Klasse muss wiederum öffentlich aus der Vorlage abgeleitet werden. so was:Beachten Sie dabei, dass das Objekt, das Sie aufrufen
shared_from_this
, einemshared_ptr
Objekt gehören muss. Das wird nicht funktionieren:quelle
shared_ptr<S> sp1(new S);
es bevorzugt zu verwendenshared_ptr<S> sp1 = make_shared<S>();
, siehe zum Beispiel stackoverflow.com/questions/18301511/…shared_ptr<S> sp2 = p->not_dangerous();
da die Gefahr hier besteht, dass Sie einen shared_ptr auf normale Weise erstellen müssen, bevor Sieshared_from_this()
das erste Mal aufrufen ! Das ist wirklich leicht falsch zu machen! Vor C ++ 17 muss UB aufrufen,shared_from_this()
bevor genau ein shared_ptr auf normale Weise erstellt wurde:auto sptr = std::make_shared<S>();
odershared_ptr<S> sptr(new S());
. Zum Glück wird dies ab C ++ 17 geworfen.S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- Es ist zulässig, shared_from_this nur für ein zuvor freigegebenes Objekt aufzurufen, dh für ein Objekt, das von std :: shared_ptr <T> verwaltet wird. Andernfalls ist das Verhalten undefiniert (bis C ++ 17). Std :: bad_weak_ptr wird ausgelöst (vom Shared_ptr-Konstruktor aus einem standardmäßig konstruierten schwachen_Das) (seit C ++ 17). . Die Realität ist also, dass es aufgerufen werden solltealways_dangerous()
, weil Sie wissen müssen, ob es bereits geteilt wurde oder nicht.Hier ist meine Erklärung aus der Perspektive der Schrauben und Muttern (die oberste Antwort hat bei mir nicht 'geklickt'). * Beachten Sie, dass dies das Ergebnis der Untersuchung der Quelle für shared_ptr und enable_shared_from_this ist, die mit Visual Studio 2012 geliefert wird. Möglicherweise implementieren andere Compiler enable_shared_from_this anders ... *
enable_shared_from_this<T>
fügt eine privateweak_ptr<T>
Instanz hinzu,T
die den ' one true reference count ' für die Instanz von enthältT
.Wenn Sie also zum ersten Mal ein
shared_ptr<T>
auf einem neuen T * erstellen , wird das interne schwache_ptr dieses T * mit einem Refcount von 1 initialisiert. Das Neue setzt imshared_ptr
Grunde darauf zurückweak_ptr
.T
kann dann in seinen Methoden aufrufenshared_from_this
, um eine Instanz davon zu erhaltenshared_ptr<T>
, die auf denselben intern gespeicherten Referenzzähler zurückgreift . Auf diese Weise haben Sie immer einen Ort, an demT*
die Ref-Zählung gespeichert wird, anstatt mehrereshared_ptr
Instanzen zu haben, die nichts voneinander wissen, und jeder denkt, dass sieshared_ptr
diejenige sind, die für das Ref-ZählenT
und Löschen verantwortlich ist, wenn ihre Ref -count erreicht Null.quelle
So, when you first create...
dass dies eine Anforderung ist (wie Sie sagen, wird der schwache_PTR erst initialisiert, wenn Sie den Objektzeiger an einen gemeinsam genutzten_PTR-Ctor übergeben!) Und in dieser Anforderung können Dinge schrecklich schief gehen, wenn Sie es sind nicht vorsichtig. Wenn Sie vor dem Aufruf kein shared_ptr erstellen, erhaltenshared_from_this
Sie UB. Wenn Sie mehr als ein shared_ptr erstellen, erhalten Sie auch UB. Sie müssen irgendwie sicherstellen, dass Sie einen shared_ptr genau einmal erstellen .enable_shared_from_this
ist anfangs spröde, da es darum geht, ashared_ptr<T>
von a zu erhaltenT*
, aber in WirklichkeitT* t
ist es im Allgemeinen nicht sicher, wenn Sie einen Zeiger erhalten, davon auszugehen, dass er bereits geteilt wurde oder nicht, und Die falsche Vermutung ist UB.Beachten Sie, dass die Verwendung von boost :: intrusive_ptr nicht unter diesem Problem leidet. Dies ist oft eine bequemere Möglichkeit, um dieses Problem zu umgehen.
quelle
enable_shared_from_this
Sie können mit einer API arbeiten, die dies ausdrücklich akzeptiertshared_ptr<>
. Meiner Meinung nach macht eine solche API normalerweise etwas falsch (da es besser ist, etwas Höheres im Stapel den Speicher besitzen zu lassen), aber wenn Sie gezwungen sind, mit einer solchen API zu arbeiten, ist dies eine gute Option.In c ++ 11 und höher ist es genau dasselbe: Es soll die Möglichkeit ermöglichen,
this
als gemeinsam genutzter Zeiger zurückzukehren, dathis
Sie einen Rohzeiger erhalten.Mit anderen Worten, Sie können Code wie folgt drehen
das sehr gut finden:
quelle
shared_ptr
. Möglicherweise möchten Sie die Benutzeroberfläche ändern, um sicherzustellen, dass dies der Fall ist.std::shared_ptr<Node> getParent const()
würde ich es normalerweiseNodePtr getParent const()
stattdessen als stattdessen aussetzen . Wenn Sie unbedingt Zugriff auf den internen Raw-Zeiger benötigen (bestes Beispiel: Umgang mit einer C-Bibliothek), gibt esstd::shared_ptr<T>::get
dafür etwas, das ich nicht gerne erwähne, weil ich diesen Raw-Zeiger-Accessor aus dem falschen Grund zu oft verwendet habe.Eine andere Möglichkeit besteht darin, ein
weak_ptr<Y> m_stub
Mitglied zum hinzuzufügenclass Y
. Dann schreibe:Nützlich, wenn Sie die Klasse, aus der Sie stammen, nicht ändern können (z. B. die Bibliothek anderer Personen erweitern). Vergessen Sie nicht, das Mitglied zu initialisieren, z. B. durch
m_stub = shared_ptr<Y>(this)
, es ist auch während eines Konstruktors gültig.Es ist in Ordnung, wenn mehr Stubs wie dieser in der Vererbungshierarchie vorhanden sind. Dies verhindert nicht die Zerstörung des Objekts.
Bearbeiten: Wie vom Benutzer nobar korrekt angegeben, würde der Code das Y-Objekt zerstören, wenn die Zuweisung abgeschlossen ist und temporäre Variablen zerstört werden. Daher ist meine Antwort falsch.
quelle
shared_ptr<>
der seinen Pointee nicht löscht, ist dies ein Overkill. Sie können einfach sagen,return shared_ptr<Y>(this, no_op_deleter);
wono_op_deleter
sich ein unäres Funktionsobjekt befindetY*
, das nichts nimmt und tut.m_stub = shared_ptr<Y>(this)
erstellt und zerstört sofort einen temporären shared_ptr daraus. Wenn diese Anweisung beendet ist,this
wird sie gelöscht und alle nachfolgenden Verweise baumeln.enable_shared_from_this
, behält sie einweak_ptr
von sich selbst (vom ctor ausgefüllt) bei, dasshared_ptr
beim Aufruf zurückgegeben wirdshared_from_this
. Mit anderen Worten, Sie duplizieren das, wasenable_shared_from_this
bereits vorhanden ist.