Ok, das letzte Mal, als ich C ++ für std::auto_ptr
meinen Lebensunterhalt schrieb, war alles, was die Standardbibliothek zur Verfügung hatte, und boost::shared_ptr
war der letzte Schrei. Ich habe mich nie wirklich mit den anderen Boosts für intelligente Zeigertypen befasst. Ich verstehe, dass C ++ 11 jetzt einige der Typen bietet, die Boost entwickelt hat, aber nicht alle.
Hat jemand einen einfachen Algorithmus, um zu bestimmen, wann welcher intelligente Zeiger verwendet werden soll? Vorzugsweise mit Hinweisen zu dummen Zeigern (wie rohen Zeigern T*
) und dem Rest der Boost-Smart-Zeiger. (So etwas wie das wäre toll).
Antworten:
Shared Ownership:
Der
shared_ptr
undweak_ptr
der angenommene Standard entsprechen weitgehend denen der Boost-Kollegen . Verwenden Sie sie, wenn Sie eine Ressource freigeben müssen und nicht wissen, welche als letzte am Leben sein wird. Verwenden Sieweak_ptr
diese Option , um die gemeinsam genutzte Ressource zu beobachten, ohne ihre Lebensdauer zu beeinflussen, und um Zyklen nicht zu unterbrechen. Zyklen mitshared_ptr
sollten normalerweise nicht stattfinden - zwei Ressourcen können sich nicht besitzen.Beachten Sie, dass Boost zusätzlich Angebote bietet
shared_array
, die eine geeignete Alternative zu sein könntenshared_ptr<std::vector<T> const>
.Als nächstes bietet Boost Angebote an
intrusive_ptr
, die eine einfache Lösung darstellen, wenn Ihre Ressource bereits ein Management mit Referenzzählung bietet und Sie es in das RAII-Prinzip übernehmen möchten. Dieser wurde vom Standard nicht übernommen.Einzigartiges Eigentum:
Boost hat auch ein
scoped_ptr
, das nicht kopierbar ist und für das Sie keinen Deleter angeben können.std::unique_ptr
istboost::scoped_ptr
auf Steroiden und sollte Ihre Standardwahl sein, wenn Sie einen intelligenten Zeiger benötigen . Es ermöglicht Ihnen, einen Deleter in seinen Vorlagenargumenten anzugeben und ist im Gegensatz zu beweglichboost::scoped_ptr
. Es kann auch vollständig in STL-Containern verwendet werden, solange Sie keine Vorgänge verwenden, für die (offensichtlich) kopierbare Typen erforderlich sind.Beachten Sie erneut, dass Boost eine Array-Version hat :
scoped_array
, die der Standard vereinheitlicht, indem einestd::unique_ptr<T[]>
teilweise Spezialisierung erforderlich ist, diedelete[]
den Zeiger anstelle desdelete
(mit demdefault_delete
r) verwendet.std::unique_ptr<T[]>
bietet auchoperator[]
anstelle vonoperator*
undoperator->
.Beachten Sie, dass dies
std::auto_ptr
noch im Standard enthalten ist, jedoch veraltet ist .§D.10 [depr.auto.ptr]
Kein Besitz:
Verwenden Sie dumme Zeiger (Rohzeiger) oder Referenzen für nicht besitzende Verweise auf Ressourcen und wenn Sie wissen, dass die Ressource das referenzierende Objekt / den überlebenden Bereich überlebt . Bevorzugen Sie Referenzen und verwenden Sie Rohzeiger, wenn Sie entweder Nullbarkeit oder Rücksetzbarkeit benötigen.
Wenn Sie eine nicht-besitzende Referenz auf eine Ressource, aber Sie wissen nicht , ob die Ressource das Objekt überleben wird , dass Verweise darauf in einer die Ressource packen
shared_ptr
und verwendenweak_ptr
- Sie können testen , ob die Elternshared_ptr
leben mitlock
, die Geben Sie a zurückshared_ptr
, das nicht null ist, wenn die Ressource noch vorhanden ist. Wenn Sie testen möchten, ob die Ressource tot ist, verwenden Sieexpired
. Die beiden mögen ähnlich klingen, unterscheiden sich jedoch bei gleichzeitiger Ausführung erheblich, daexpired
nur der Rückgabewert für diese einzelne Anweisung garantiert wird. Ein scheinbar unschuldiger Test wieist eine mögliche Rennbedingung.
quelle
shared_ptr
und den nicht besitzenden Zeiger neu zu schreiben aweak_ptr
...shared_array<T>
ist eine Alternative zushared_ptr<T[]>
nicht zushared_ptr<vector<T>>
: es kann nicht wachsen.Die Entscheidung, welcher intelligente Zeiger verwendet werden soll, ist eine Frage des Eigentums . Wenn es um die Ressourcenverwaltung geht, besitzt Objekt A Objekt B, wenn es die Lebensdauer von Objekt B kontrolliert. Beispielsweise gehören Mitgliedsvariablen ihren jeweiligen Objekten, da die Lebensdauer von Mitgliedsvariablen an die Lebensdauer des Objekts gebunden ist. Sie wählen intelligente Zeiger basierend darauf, wie das Objekt gehört.
Beachten Sie, dass das Eigentum an einem Softwaresystem vom Eigentum getrennt ist, da wir es außerhalb von Software betrachten würden. Zum Beispiel könnte eine Person ihr Haus "besitzen", aber das bedeutet nicht unbedingt, dass ein
Person
Objekt die Kontrolle über die Lebensdauer einesHouse
Objekts hat. Das Zusammenführen dieser realen Konzepte mit Softwarekonzepten ist ein sicherer Weg, sich selbst in ein Loch zu programmieren.Wenn Sie das alleinige Eigentum an dem Objekt haben, verwenden Sie
std::unique_ptr<T>
.Wenn Sie das Eigentum gemeinsam genutzt haben ...
- Wenn keine Eigentumszyklen vorhanden sind, verwenden Sie
std::shared_ptr<T>
.- Wenn es Zyklen gibt, definieren Sie eine "Richtung" und verwenden Sie
std::shared_ptr<T>
in die eine undstd::weak_ptr<T>
in die andere Richtung.Wenn Ihnen das Objekt gehört, Sie jedoch möglicherweise keinen Eigentümer haben, verwenden Sie normale Zeiger
T*
(z. B. übergeordnete Zeiger).Wenn das Objekt Ihnen gehört (oder anderweitig die Existenz garantiert hat), verwenden Sie Referenzen
T&
.Vorsichtsmaßnahme: Beachten Sie die Kosten für intelligente Zeiger. In speicher- oder leistungsbeschränkten Umgebungen kann es vorteilhaft sein, nur normale Zeiger mit einem manuelleren Schema für die Speicherverwaltung zu verwenden.
Die Kosten:
std::shared_ptr
hat den Overhead eines Referenzzählungsinkrements beim Kopieren plus eines Dekrements bei der Zerstörung, gefolgt von einer 0-Zählprüfung mit Löschen des gehaltenen Objekts. Abhängig von der Implementierung kann dies Ihren Code aufblähen und Leistungsprobleme verursachen.Beispiele:
Ein binärer Baum besitzt nicht seinen übergeordneten Baum, aber die Existenz eines Baums impliziert die Existenz seines übergeordneten Baums (oder
nullptr
für root), sodass ein normaler Zeiger verwendet wird. Ein binärer Baum (mit Wertesemantik) hat das alleinige Eigentum an seinen untergeordneten Elementenstd::unique_ptr
.Hier besitzt der Listenknoten seine nächsten und vorherigen Listen, daher definieren wir eine Richtung und verwenden sie
shared_ptr
für next undweak_ptr
für prev, um den Zyklus zu unterbrechen.quelle
shared_ptr<BinaryTree>
für die Kinder undweak_ptr<BinaryTree>
für die Elternbeziehung zu verwenden.Verwenden Sie die
unique_ptr<T>
ganze Zeit, außer wenn Sie eine Referenzzählung benötigen. In diesem Fall verwenden Sieshared_ptr<T>
(und in sehr seltenen Fällen,weak_ptr<T>
um Referenzzyklen zu vermeiden). In fast allen Fällen ist übertragbares einzigartiges Eigentum in Ordnung.Rohe Zeiger: Nur dann gut, wenn Sie kovariante Renditen benötigen. Sie sind sonst nicht besonders nützlich.
Array-Zeiger:
unique_ptr
hat eine Spezialisierung, fürT[]
diedelete[]
das Ergebnis automatisch aufgerufen wird , sodass Sie diesunique_ptr<int[]> p(new int[42]);
beispielsweise sicher tun können .shared_ptr
Sie benötigen weiterhin einen benutzerdefinierten Löscher, benötigen jedoch keinen speziellen gemeinsam genutzten oder eindeutigen Array-Zeiger. Natürlich werden solche Dinge normalerweise am besten durchstd::vector
sowieso ersetzt. Bietet leidershared_ptr
keine Array-Zugriffsfunktion, sodass Sie immer noch manuell aufrufen müssenget()
,unique_ptr<T[]>
bietet jedochoperator[]
anstelle vonoperator*
undoperator->
. In jedem Fall müssen Sie sich selbst überprüfen. Dies machtshared_ptr
etwas weniger benutzerfreundlich, obwohl wohl der generische Vorteil und keine Boost-Abhängigkeit machtunique_ptr
undshared_ptr
die Gewinner wieder.Zeiger mit Gültigkeitsbereich: irrelevant gemacht durch
unique_ptr
, genau wieauto_ptr
.Da ist wirklich nichts mehr dran. In C ++ 03 ohne Verschiebungssemantik war diese Situation sehr kompliziert, aber in C ++ 11 ist der Rat sehr einfach.
Es gibt immer noch Verwendungen für andere intelligente Zeiger wie
intrusive_ptr
oderinterprocess_ptr
. Sie sind jedoch sehr nisch und im allgemeinen Fall völlig unnötig.quelle
std::unique_ptr<T[]>
bietetoperator[]
anstelle vonoperator*
undoperator->
. Es ist wahr, dass Sie immer noch gebundene Überprüfungen durchführen müssen.Fälle, wann zu verwenden
unique_ptr
:Fälle, wann zu verwenden
shared_ptr
:Fälle, wann zu verwenden
weak_ptr
:Fühlen Sie sich frei zu bearbeiten und weitere hinzuzufügen
quelle