wake-up-neo.com

Smart Pointer (Boost) erklärt

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?

214
Sasha

Grundlegende Eigenschaften von intelligenten Zeigern

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

  • überhaupt kein Eigentum
  • Eigentumsübergang
  • Eigentumsanteil

Das erste bedeutet, dass ein Smart Pointer das Objekt nicht löschen kann, weil es ihm nicht gehört. Der 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 das Eigentumsrecht beispielsweise auf den zurückgegebenen Smart Pointer übertragen.

Der dritte Punkt bedeutet, dass mehrere intelligente Zeiger gleichzeitig auf dasselbe Objekt zeigen können. Dies gilt auch für einen rohen Zeiger, jedoch fehlt ein wichtiges Merkmal bei rohen Zeigern: Sie definieren nicht, ob sie besitzen sind oder nicht. Ein intelligenter Zeiger für die Teilhaberschaft löscht das Objekt, wenn jeder Eigentümer das Objekt aufgibt. Dieses Verhalten wird häufig benötigt, sodass gemeinsame Smart Pointer weit verbreitet sind.

Einige Besitzer intelligenter Zeiger unterstützen weder den zweiten noch den dritten. Sie können daher nicht von Funktionen zurückgegeben oder an eine andere Stelle übergeben werden. Dies ist am besten für RAII Zwecke geeignet, bei denen der Smart Pointer lokal gehalten und nur so erstellt wird, dass ein Objekt freigegeben wird, nachdem es den Gültigkeitsbereich verlässt.

Die Eigentumsanteile können mit einem Kopierkonstruktor implementiert werden. Dadurch wird natürlich ein intelligenter Zeiger kopiert, und sowohl die Kopie als auch das Original verweisen auf dasselbe Objekt. Die Übertragung des Eigentums kann derzeit in C++ nicht wirklich implementiert werden, da es keine Möglichkeit gibt, etwas von einem Objekt auf ein anderes zu übertragen, was von der Sprache unterstützt wird: Wenn Sie versuchen, ein Objekt von einer Funktion zurückzugeben, geschieht, dass das Objekt kopiert wird. Ein intelligenter Zeiger, der die Übertragung des Eigentums implementiert, muss daher den Kopierkonstruktor verwenden, um diese Übertragung des Eigentums zu implementieren. Dies bricht jedoch wiederum seine Verwendung in Containern, da Anforderungen ein bestimmtes Verhalten des Kopierkonstruktors von Elementen von Containern angeben, das mit diesem sogenannten "Verschiebungskonstruktor" -Verhalten dieser intelligenten Zeiger nicht kompatibel ist.

C++ 1x bietet native Unterstützung für die Übertragung des Eigentums, indem sogenannte "Verschiebungskonstruktoren" und "Verschiebungszuweisungsoperatoren" eingeführt werden. Es kommt auch mit einem solchen Transfer-of-Ownership-Smart Pointer namens unique_ptr.

Kategorisieren von intelligenten Zeigern

scoped_ptr ist ein intelligenter Zeiger, der weder übertragbar noch gemeinsam nutzbar ist. Es kann nur verwendet werden, wenn Sie lokal Speicher zuweisen müssen. Stellen Sie jedoch sicher, dass der Speicher wieder freigegeben wird, wenn er außerhalb des Gültigkeitsbereichs liegt. Es kann aber trotzdem mit einem anderen scoped_ptr ausgetauscht werden, wenn Sie dies möchten.

shared_ptr ist ein intelligenter Zeiger, der sich das Eigentum teilt (dritte Art oben). Die Referenz wird gezählt, um festzustellen, wann die letzte Kopie des Objekts den Gültigkeitsbereich verlässt, und um das verwaltete Objekt freizugeben.

weak_ptr ist ein nicht im Besitz von Smart Pointer. Es wird verwendet, um auf ein verwaltetes Objekt (das von einem shared_ptr verwaltet wird) zu verweisen, ohne eine Referenzanzahl hinzuzufügen. Normalerweise müssten Sie den rohen Zeiger aus dem shared_ptr holen und diesen herum kopieren. Dies wäre jedoch nicht sicher, da Sie nicht überprüfen können, wann das Objekt tatsächlich gelöscht wurde. Somit bietet weak_ptr die Möglichkeit, auf ein von shared_ptr verwaltetes Objekt zu verweisen. Wenn Sie auf das Objekt zugreifen müssen, können Sie dessen Verwaltung sperren (um zu vermeiden, dass in einem anderen Thread ein shared_ptr es freigibt, während Sie das Objekt verwenden) und es dann verwenden. Wenn der weak_ptr auf ein bereits gelöschtes Objekt zeigt, werden Sie durch Auslösen einer Ausnahme darauf hingewiesen. Die Verwendung von weak_ptr ist am vorteilhaftesten, wenn Sie eine zyklische Referenz haben: Die Referenzzählung kann mit einer solchen Situation nicht einfach fertig werden.

intrusive_ptr ist wie ein shared_ptr, behält jedoch nicht den Referenzzähler in einem shared_ptr bei, sondern überlässt das Inkrementieren/Dekrementieren des Zählers einigen Hilfsfunktionen, die von dem verwalteten Objekt definiert werden müssen. Dies hat den Vorteil, dass ein bereits referenziertes Objekt (dessen Referenzzahl durch einen externen Referenzzählmechanismus erhöht wird) in einen intrusive_ptr eingefügt werden kann, da die Referenzzahl nicht mehr im Smart Pointer enthalten ist, sondern einen vorhandenen Smart Pointer verwendet Referenzzählmechanismus.

unique_ptr ist ein Zeiger auf Eigentumsübertragung. Sie können es nicht kopieren, aber Sie können es mit den move-Konstruktoren von C++ 1x verschieben:

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 der fehlenden systemeigenen Unterstützung für das Verschieben können sie nicht ohne Probleme bereitgestellt werden. unique_ptr stiehlt automatisch Ressourcen aus einem temporären anderen unique_ptr, einem der Hauptmerkmale der Bewegungssemantik. auto_ptr wird in der nächsten Version von C++ Standard zugunsten von unique_ptr veraltet sein. Mit C++ 1x können auch Objekte gefüllt werden, die nur beweglich sind, aber nicht in Container kopiert werden können. So können Sie beispielsweise unique_ptrs in einen Vektor einfügen. Ich höre hier auf und verweise auf einen schönen Artikel darüber, wenn Sie mehr darüber lesen möchten.

334

scoped_ptr ist das einfachste. Wenn es außerhalb des Gültigkeitsbereichs liegt, wird es zerstört. Der folgende Code ist illegal (scoped_ptrs können nicht kopiert werden), veranschaulicht jedoch einen Punkt:

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. Bei jeder Kopie oder Zuweisung wird der Referenzzähler erhöht. 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

weak_ptr ist ein schwacher Verweis auf einen gemeinsam genutzten Zeiger, bei dem Sie überprüfen müssen, ob der Verweis auf shared_ptr 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 
}

intrusive_ptr wird normalerweise verwendet, wenn Sie einen Smart-Ptr eines Drittanbieters verwenden müssen. Es wird eine kostenlose Funktion zum Hinzufügen und Verringern des Referenzzählers aufgerufen. Weitere Informationen finden Sie unter Link , um die Dokumentation zu verbessern.

90
Doug T.

Übersehen Sie nicht boost::ptr_container in jeder Umfrage zu intelligenten Boost-Zeigern. Sie können in Situationen von unschätzbarem Wert sein, in denen beispielsweise std::vector<boost::shared_ptr<T> > Zu langsam wäre.

20
timday

Ich bin dem Rat über das Betrachten der Dokumentation gefolgt. 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 Zuordnung möglich, jedoch kein 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 (lies die Dokumentation und suche auf Google nach Nice picture;)
  • shared_ptr - der generische, leistungsstärkste (und schwerste) der intelligenten Zeiger (von den durch boost angebotenen Zeigern)
  • Es gibt auch alte auto_ptr, dies stellt sicher, dass das Objekt, auf das es zeigt, automatisch zerstört wird, wenn die Kontrolle einen Bereich verlässt. Es hat jedoch eine andere Kopiersemantik als der Rest der Jungs.
  • unique_ptr - wird mit C++ 0x geliefert

Antwort zum Bearbeiten: Ja

12
Anonymous