Smart Pointer (Boost) erklärt

Lesezeit: 9 Minuten

Was ist der Unterschied zwischen den folgenden Zeigern? Wann verwenden Sie jeden Zeiger im Produktionscode, wenn überhaupt?

Beispiele wären willkommen!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Verwenden Sie Boost im Produktionscode?

Smart Pointer Boost erklart
Johannes Schaub – litb

Grundlegende Eigenschaften von intelligenten Zeigern

Es ist einfach, wenn Sie Eigenschaften haben, die Sie jedem intelligenten Zeiger zuweisen können. Es gibt drei wichtige Eigenschaften.

  • überhaupt kein Besitz
  • Übertragung des Eigentums
  • Anteil des Eigentums

Das erste bedeutet, dass ein intelligenter Zeiger das Objekt nicht löschen kann, da es ihm nicht gehört. Das zweite bedeutet, dass immer nur ein Smart Pointer gleichzeitig auf dasselbe Objekt zeigen kann. Wenn der Smart Pointer von Funktionen zurückgegeben werden soll, wird beispielsweise das Eigentum an dem zurückgegebenen Smart Pointer übertragen.

Das dritte bedeutet, dass mehrere intelligente Zeiger gleichzeitig auf dasselbe Objekt zeigen können. Dies gilt für a roher Zeiger auch, jedoch fehlt rohen Zeigern ein wichtiges Merkmal: Sie definieren nicht, ob sie es sind besitzen oder nicht. Ein Smart-Pointer zur Anteilseigentümerschaft löscht das Objekt, wenn jeder Eigentümer das Objekt aufgibt. Dieses Verhalten wird häufig benötigt, daher sind Shared Ownership Smart Pointer weit verbreitet.

Einige Besitzer von Smart Pointern unterstützen weder die zweite noch die dritte. Sie können daher nicht von Funktionen zurückgegeben oder an anderer Stelle übergeben werden. Welche ist am besten geeignet für RAII Zwecke, bei denen der intelligente Zeiger lokal gehalten und nur erstellt wird, damit er ein Objekt freigibt, nachdem es den Gültigkeitsbereich verlässt.

Share of Ownership kann durch einen Kopierkonstruktor implementiert werden. Dadurch wird natürlich ein intelligenter Zeiger kopiert, und sowohl die Kopie als auch das Original referenzieren dasselbe Objekt. Die Eigentumsübertragung kann derzeit in C++ nicht wirklich implementiert werden, da es keine von der Sprache unterstützte Möglichkeit gibt, etwas von einem Objekt auf ein anderes zu übertragen: Wenn Sie versuchen, ein Objekt von einer Funktion zurückzugeben, wird das Objekt kopiert. Ein intelligenter Zeiger, der die Eigentumsübertragung implementiert, muss also den Kopierkonstruktor verwenden, um diese Eigentumsübertragung zu implementieren. Dies unterbricht jedoch wiederum seine Verwendung in Containern, da Anforderungen ein bestimmtes Verhalten des Kopierkonstruktors von Elementen von Containern angeben, das mit diesem sogenannten “Moving Constructor”-Verhalten dieser intelligenten Zeiger nicht kompatibel ist.

C++1x bietet native Unterstützung für die Eigentumsübertragung durch die Einführung sogenannter „Bewegungskonstruktoren“ und „Bewegungszuweisungsoperatoren“. Es kommt auch mit einem solchen Smart Pointer für die Eigentumsübertragung, der als “Smart Pointer” bezeichnet wird unique_ptr.

Smart Pointer kategorisieren

scoped_ptr ist ein intelligenter Zeiger, der weder übertragbar noch teilbar ist. Es kann nur verwendet werden, wenn Sie lokal Speicher zuweisen müssen, aber stellen Sie sicher, dass es wieder freigegeben wird, wenn es den Bereich verlässt. Aber es kann immer noch mit einem anderen scoped_ptr ausgetauscht werden, wenn Sie dies wünschen.

shared_ptr ist ein intelligenter Zeiger, der den Besitz teilt (dritte Art oben). Es wird referenziert, sodass es sehen kann, wann die letzte Kopie davon den Gültigkeitsbereich verlässt, und dann das verwaltete Objekt freigibt.

weak_ptr ist ein nicht besitzender intelligenter Zeiger. Es wird verwendet, um auf ein verwaltetes Objekt (verwaltet von einem shared_ptr) zu verweisen, ohne einen Referenzzähler hinzuzufügen. Normalerweise müssten Sie den Raw-Zeiger aus dem shared_ptr holen und diesen herumkopieren. Dies wäre jedoch nicht sicher, da Sie nicht überprüfen könnten, wann das Objekt tatsächlich gelöscht wurde. Weak_ptr stellt also Mittel bereit, indem es auf ein Objekt verweist, das von shared_ptr verwaltet wird. Wenn Sie auf das Objekt zugreifen müssen, können Sie dessen Verwaltung sperren (um zu vermeiden, dass ein shared_ptr es in einem anderen Thread freigibt, während Sie das Objekt verwenden) und es dann verwenden. Wenn das schwache_ptr auf ein bereits gelöschtes Objekt zeigt, wird es Sie durch das Auslösen einer Ausnahme bemerken. Weak_ptr zu verwenden ist am vorteilhaftesten, wenn Sie eine zyklische Referenz haben: Die Referenzzählung kann mit einer solchen Situation nicht ohne weiteres fertig werden.

intrusive_ptr ist wie ein shared_ptr, behält aber nicht den Referenzzähler in einem shared_ptr bei, sondern überlässt das Erhöhen/Dekrementieren des Zählers einigen Hilfsfunktionen, die von dem verwalteten Objekt definiert werden müssen. Dies hat den Vorteil, dass ein bereits referenziertes Objekt (das einen Referenzzähler hat, der durch einen externen Referenzzählmechanismus erhöht wird) in einen intrusive_ptr gestopft werden kann – da der Referenzzähler nicht mehr intern für den Smart Pointer ist, sondern der Smart Pointer einen vorhandenen verwendet Referenzzählmechanismus.

unique_ptr ist ein Besitzübertragungszeiger. Sie können es nicht kopieren, aber Sie können es verschieben, indem Sie die Move-Konstruktoren von C++1x verwenden:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

Dies ist die Semantik, die std::auto_ptr befolgt, aber aufgrund fehlender nativer Unterstützung für das Verschieben nicht ohne Fallstricke bereitstellt. unique_ptr stiehlt automatisch Ressourcen von einem temporären anderen unique_ptr, was eines der Hauptmerkmale der Move-Semantik ist. auto_ptr wird in der nächsten C++-Standardversion zugunsten von unique_ptr veraltet sein. C++1x ermöglicht auch das Füllen von Objekten, die nur verschiebbar, aber nicht in Container kopierbar sind. So können Sie zum Beispiel unique_ptr’s in einen Vektor stopfen. Ich höre hier auf und verweise auf Sie ein feiner artikel darüber, wenn Sie mehr darüber lesen möchten.

  • danke für das lob dude. Ich weiß es zu schätzen, also bekommst du jetzt auch +1 :p

    – Johannes Schaub – litb

    20. Februar 2009 um 16:02 Uhr

  • @litb: Ich habe Zweifel an der “Eigentumsübertragung”; Ich stimme zu, dass es keine gibt Real Eigentumsübertragung zwischen Objekten in C ++ 03, aber für intelligente Zeiger kann dies nicht getan werden, durch die destruktive Kopie Mechanismus hier angegeben informit.com/articles/article.aspx?p=31529&seqNum=5.

    – legends2k

    18. März 2010 um 18:12 Uhr


  • fantastische Antwort. Notiz: auto_ptr ist bereits veraltet (C++11).

    – Nickolay

    5. Januar 2012 um 12:37 Uhr

  • “Dies unterbricht wiederum seine Verwendung in Containern, da Anforderungen ein bestimmtes Verhalten des Kopierkonstruktors von Elementen von Containern angeben, das mit diesem sogenannten “Moving Constructor” -Verhalten dieser intelligenten Zeiger nicht kompatibel ist.” Habe das Teil nicht bekommen.

    – Raja

    17. Oktober 2013 um 7:06 Uhr

  • Das wurde mir auch gesagt intrusive_ptr kann vorzuziehen sein shared_ptr für eine bessere Cache-Kohärenz. Anscheinend funktioniert der Cache besser, wenn Sie die Referenzzählung als Teil des Speichers des verwalteten Objekts selbst statt als separates Objekt speichern. Dies kann in einer Vorlage oder Oberklasse des verwalteten Objekts implementiert werden.

    – Eliot

    2. Dezember 2013 um 22:57 Uhr


Smart Pointer Boost erklart
Doug T.

scoped_ptr ist das einfachste. Wenn es den Geltungsbereich verlässt, wird es zerstört. Der folgende Code ist illegal (scoped_ptrs sind nicht kopierbar), wird aber einen Punkt veranschaulichen:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr wird als Referenz gezählt. Jedes Mal, wenn eine Kopie oder Zuweisung auftritt, wird der Referenzzähler inkrementiert. Jedes Mal, wenn der Destruktor einer Instanz ausgelöst wird, wird der Referenzzähler für das rohe T* dekrementiert. Sobald es 0 ist, wird der Zeiger freigegeben.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

schwach_ptr ist ein schwacher Verweis auf einen gemeinsam genutzten Zeiger, bei dem Sie überprüfen müssen, ob der shared_ptr, auf den gezeigt wird, noch vorhanden ist

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

aufdringlich_ptr wird normalerweise verwendet, wenn Sie einen intelligenten PTR eines Drittanbieters verwenden müssen. Es ruft eine freie Funktion auf, um den Referenzzähler zu addieren und zu dekrementieren Verknüpfung um die Dokumentation für weitere Informationen zu verbessern.

  • ist nicht if (tPtrAccessed[0].get() == 0) Sollte sein if (tPtrAccessed.get() == 0) ?

    – Rajeshwar

    23. November 2014 um 4:14 Uhr

  • @DougT. Glauben Sie, dass Java die gleiche Idee mit Referenzen verwendet? Weich, hart, schwach usw.?

    – gansub

    10. April 2019 um 3:34 Uhr


Nicht übersehen boost::ptr_container in jeder Umfrage von Boost Smart Pointer. Sie können in Situationen von unschätzbarem Wert sein, in denen ein zB std::vector<boost::shared_ptr<T> > wäre zu langsam.

  • Als ich es das letzte Mal ausprobierte, zeigte das Benchmarking tatsächlich, dass sich die Leistungslücke deutlich geschlossen hatte, seit ich dies ursprünglich geschrieben hatte, zumindest auf typischer PC-HW! Der effizientere ptr_container-Ansatz kann jedoch in Nischenanwendungsfällen immer noch einige Vorteile haben.

    – uhrtag

    8. August 2016 um 12:21 Uhr

Smart Pointer Boost erklart
Anonym

Ich schließe mich dem Rat an, sich die Dokumentation anzusehen. Es ist nicht so beängstigend, wie es scheint. Und ein paar kurze Hinweise:

  • scoped_ptr – ein Zeiger, der automatisch gelöscht wird, wenn er den Gültigkeitsbereich verlässt. Hinweis – keine Zuweisung möglich, führt aber zu keinem Overhead
  • intrusive_ptr – Referenzzählzeiger ohne Overhead von smart_ptr. Das Objekt selbst speichert jedoch den Referenzzähler
  • weak_ptr – arbeitet zusammen mit shared_ptr um mit den Situationen umzugehen, die zu zirkulären Abhängigkeiten führen (lesen Sie die Dokumentation und suchen Sie bei Google nach einem schönen Bild;)
  • shared_ptr – der generische, leistungsstärkste (und schwerste) der Smart Pointer (von denen, die von Boost angeboten werden)
  • Es gibt auch alte auto_ptr, das sicherstellt, dass das Objekt, auf das es zeigt, automatisch zerstört wird, wenn die Steuerung einen Bereich verlässt. Es hat jedoch eine andere Kopiersemantik als der Rest der Jungs.
  • unique_ptrwird mit C++0x kommen

Antwort auf Bearbeitung:
Jawohl

963520cookie-checkSmart Pointer (Boost) erklärt

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy