Antworten:
Es ist einfach, wenn Sie Eigenschaften haben, denen Sie jeden intelligenten Zeiger zuweisen können. Es gibt drei wichtige Eigenschaften.
Der erste bedeutet, dass ein intelligenter Zeiger das Objekt nicht löschen kann, weil er es nicht besitzt. Der zweite bedeutet, dass immer nur ein intelligenter Zeiger gleichzeitig auf dasselbe Objekt zeigen kann. Wenn der Smart Pointer von Funktionen zurückgegeben werden soll, wird der Besitz beispielsweise auf den zurückgegebenen Smart Pointer übertragen.
Der dritte bedeutet, dass mehrere intelligente Zeiger gleichzeitig auf dasselbe Objekt zeigen können. Dies gilt auch für einen Rohzeiger. Rohzeigern fehlt jedoch eine wichtige Funktion: Sie definieren nicht, ob sie Eigentümer sind oder nicht. Ein Smart Pointer mit Eigentumsanteil löscht das Objekt, wenn jeder Eigentümer das Objekt aufgibt. Dieses Verhalten wird häufig benötigt, sodass Smart Pointer mit gemeinsamem Besitz weit verbreitet sind.
Einige Smart Pointer 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 erstellt wird, damit ein Objekt freigegeben wird, nachdem es den Gültigkeitsbereich verlassen hat.
Der Eigentumsanteil kann durch einen 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, das 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 den Kopierkonstruktor verwenden, um diese Übertragung des Eigentums zu implementieren. Dies unterbricht jedoch wiederum seine Verwendung in Containern, da die 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 Smart-Point-of-Ownership-Zeiger namens unique_ptr
.
scoped_ptr
ist ein intelligenter Zeiger, der weder übertragbar noch gemeinsam nutzbar ist. Es ist nur verwendbar, wenn Sie lokal Speicher zuweisen müssen, aber stellen Sie sicher, dass er wieder freigegeben wird, wenn er außerhalb des Gültigkeitsbereichs liegt. Es kann jedoch weiterhin mit einem anderen scoped_ptr ausgetauscht werden, wenn Sie dies wünschen.
shared_ptr
ist ein intelligenter Zeiger, der das Eigentum teilt (dritte Art oben). Es wird als Referenz gezählt, damit es sehen kann, wann die letzte Kopie den Gültigkeitsbereich verlässt, und dann das verwaltete Objekt freigibt.
weak_ptr
ist ein nicht besitzender Smart Pointer. 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 Rohzeiger aus dem shared_ptr herausholen und diesen kopieren. Dies wäre jedoch nicht sicher, da Sie nicht überprüfen können, wann das Objekt tatsächlich gelöscht wurde. Schwache_ptr bietet also Mittel, indem sie auf ein von shared_ptr verwaltetes Objekt verweist. Wenn Sie auf das Objekt zugreifen müssen, können Sie die 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 schwache_ptr auf ein bereits gelöschtes Objekt verweist, werden Sie durch Auslösen einer Ausnahme bemerkt. Die Verwendung von schwachem_ptr ist am vorteilhaftesten, wenn Sie eine zyklische Referenz haben: Die Referenzzählung kann mit einer solchen Situation nicht einfach umgehen.
intrusive_ptr
ist wie ein shared_ptr, behält jedoch nicht die Referenzanzahl in einem shared_ptr bei, sondern überlässt das Inkrementieren / Dekrementieren der Anzahl auf einige Hilfsfunktionen, die von dem verwalteten Objekt definiert werden müssen. Dies hat den Vorteil, dass ein bereits referenziertes Objekt (dessen Referenzanzahl durch einen externen Referenzzählmechanismus erhöht wird) in ein intrusive_ptr eingefügt werden kann, da der Referenzzähler nicht mehr intern im Smart Pointer ist, der Smart Pointer jedoch einen vorhandenen verwendet Referenzzählmechanismus.
unique_ptr
ist ein Zeiger auf die Übertragung des Eigentums. Sie können es nicht kopieren, aber Sie können es mithilfe der Verschiebungskonstruktoren 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, der std :: auto_ptr gehorcht, aber aufgrund der fehlenden nativen Unterstützung für das Verschieben werden sie nicht ohne Fallstricke bereitgestellt. unique_ptr stiehlt automatisch Ressourcen von einem temporären anderen unique_ptr, was eines der Hauptmerkmale der Verschiebungssemantik ist. auto_ptr wird in der nächsten C ++ Standard-Version zugunsten von unique_ptr nicht mehr unterstützt. In C ++ 1x können auch Objekte gestopft werden, die nur beweglich, aber nicht in Container kopierbar sind. So können Sie beispielsweise unique_ptrs in einen Vektor einfügen. Ich werde hier aufhören und Sie auf einen schönen Artikel darüber verweisen, wenn Sie mehr darüber lesen möchten.
auto_ptr
ist bereits veraltet (C ++ 11).
intrusive_ptr
dies shared_ptr
für eine bessere Cache-Kohärenz vorzuziehen ist . Anscheinend funktioniert der Cache besser, wenn Sie den Referenzzähler als Teil des Speichers des verwalteten Objekts selbst anstelle eines separaten Objekts speichern. Dies kann in einer Vorlage oder Oberklasse des verwalteten Objekts implementiert werden.
scoped_ptr ist das einfachste. Wenn es den Rahmen verlässt, wird es zerstört. Der folgende Code ist unzulässig (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. Jedes Mal, wenn eine Kopie oder Zuordnung erfolgt, 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
schwach_ptr ist eine schwache Referenz auf einen gemeinsam genutzten Zeiger, bei der Sie überprüfen müssen, ob der gemeinsam genutzte Zeiger 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 ein 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 dem Link zum Erhöhen der Dokumentation.
if (tPtrAccessed[0].get() == 0)
sein if (tPtrAccessed.get() == 0)
?
Übersehen Sie boost::ptr_container
in keiner Umfrage zu Boost Smart Pointern. Sie können in Situationen von unschätzbarem Wert sein, in denen ein zB std::vector<boost::shared_ptr<T> >
zu langsam wäre.
Ich stimme dem Rat zu, die Dokumentation zu lesen. Es ist nicht so beängstigend, wie es scheint. Und ein paar kurze Hinweise:
scoped_ptr
- Ein Zeiger wird automatisch gelöscht, wenn er den Gültigkeitsbereich verlässt. Hinweis - Keine Zuordnung möglich, aber kein Overheadintrusive_ptr
- Referenzzählzeiger ohne Overhead von smart_ptr
. Das Objekt selbst speichert jedoch den Referenzzählerweak_ptr
- arbeitet mit zusammen shared_ptr
, um die Situationen zu bewältigen, die zu zirkulären Abhängigkeiten führen (lesen Sie die Dokumentation und suchen Sie auf Google nach schönen Bildern;)shared_ptr
- der generische, leistungsstärkste (und schwerste) der intelligenten Zeiger (von denen, die durch Boost angeboten werden)auto_ptr
, die sicherstellen, 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_ptr
- wird mit C ++ 0x kommenAntwort zum Bearbeiten: Ja