std::enable_shared_from_this
| Definiert in Header <memory> |
||
| template< class T > class enable_shared_from_this; |
(seit C++11) | |
std::enable_shared_from_this ermöglicht es einem Objekt t, das aktuell von einem std::shared_ptr namens pt verwaltet wird, sicher zusätzliche std::shared_ptr-Instanzen pt1, pt2 usw. zu generieren, die alle die Besitzrechte an t mit pt teilen.
Durch öffentliche Vererbung von std::enable_shared_from_this<T> erhält der Typ T eine Memberfunktion shared_from_this. Wenn ein Objekt t vom Typ T von einem std::shared_ptr<T> namens pt verwaltet wird, gibt der Aufruf von T::shared_from_this einen neuen std::shared_ptr<T> zurück, der die Besitzrechte an t mit pt teilt.
Inhalt |
[bearbeiten] Datenmember
| Mitglied | Beschreibung |
mutable std::weak_ptr<T> weak_this |
Das Objekt, das den Kontrollblock des ersten gemeinsamen Besitzers von *this verfolgt. ((exposition-only member object*) |
[bearbeiten] Memberfunktionen
Konstruiert ein enable_shared_from_this-Objekt.(geschützte Memberfunktion) | |
Zerstört ein enable_shared_from_this-Objekt.(geschützte Memberfunktion) | |
| Gibt eine Referenz auf *this zurück. (geschützte Memberfunktion) | |
| Gibt einen std::shared_ptr zurück, der die Besitzrechte an *this teilt. (öffentliche Memberfunktion) | |
| (C++17) |
Gibt einen std::weak_ptr zurück, der die Besitzrechte an *this teilt. (öffentliche Memberfunktion) |
[bearbeiten] Hinweise
Die Konstruktoren von std::shared_ptr erkennen die Anwesenheit einer eindeutigen und zugänglichen (d.h. öffentliche Vererbung ist zwingend erforderlich) enable_shared_from_this-Basisklasse und weisen den neu erstellten std::shared_ptr weak_this zu, wenn er noch nicht von einem lebenden std::shared_ptr besessen wird. Das Konstruieren eines std::shared_ptr für ein Objekt, das bereits von einem anderen std::shared_ptr verwaltet wird, berücksichtigt weak_this nicht und führt daher zu undefiniertem Verhalten.
Es ist gestattet, shared_from_this nur auf einem zuvor gemeinsam besessenen Objekt aufzurufen, d.h. auf einem Objekt, das von einem std::shared_ptr<T> verwaltet wird. Andernfalls wird std::bad_weak_ptr ausgelöst (durch den std::shared_ptr-Konstruktor aus einem standardmäßig konstruierten weak_this).
enable_shared_from_this stellt die sichere Alternative zu einem Ausdruck wie std::shared_ptr<T>(this) dar, was wahrscheinlich dazu führt, dass this von mehreren nicht voneinander wissenden Besitzern mehr als einmal zerstört wird (siehe Beispiel unten).
[bearbeiten] Beispiel
#include <iostream> #include <memory> class Good : public std::enable_shared_from_this<Good> { public: std::shared_ptr<Good> getptr() { return shared_from_this(); } }; class Best : public std::enable_shared_from_this<Best> { struct Private{ explicit Private() = default; }; public: // Constructor is only usable by this class Best(Private) {} // Everyone else has to use this factory function // Hence all Best objects will be contained in shared_ptr static std::shared_ptr<Best> create() { return std::make_shared<Best>(Private()); } std::shared_ptr<Best> getptr() { return shared_from_this(); } }; struct Bad { std::shared_ptr<Bad> getptr() { return std::shared_ptr<Bad>(this); } ~Bad() { std::cout << "Bad::~Bad() called\n"; } }; void testGood() { // Good: the two shared_ptr's share the same object std::shared_ptr<Good> good0 = std::make_shared<Good>(); std::shared_ptr<Good> good1 = good0->getptr(); std::cout << "good1.use_count() = " << good1.use_count() << '\n'; } void misuseGood() { // Bad: shared_from_this is called without having std::shared_ptr owning the caller try { Good not_so_good; std::shared_ptr<Good> gp1 = not_so_good.getptr(); } catch (std::bad_weak_ptr& e) { // undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17) std::cout << e.what() << '\n'; } } void testBest() { // Best: Same but cannot stack-allocate it: std::shared_ptr<Best> best0 = Best::create(); std::shared_ptr<Best> best1 = best0->getptr(); std::cout << "best1.use_count() = " << best1.use_count() << '\n'; // Best stackBest; // <- Will not compile because Best::Best() is private. } void testBad() { // Bad, each shared_ptr thinks it is the only owner of the object std::shared_ptr<Bad> bad0 = std::make_shared<Bad>(); std::shared_ptr<Bad> bad1 = bad0->getptr(); std::cout << "bad1.use_count() = " << bad1.use_count() << '\n'; } // UB: double-delete of Bad int main() { testGood(); misuseGood(); testBest(); testBad(); }
Mögliche Ausgabe
good1.use_count() = 2 bad_weak_ptr best1.use_count() = 2 bad1.use_count() = 1 Bad::~Bad() called Bad::~Bad() called *** glibc detected *** ./test: double free or corruption
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| LWG 2179 (P0033R1) |
C++11 | Für einen Typ T, der von enable_shared_from_this abgeleitet ist, war das Verhalten beimKonstruieren von zwei std::shared_ptr<T>s aus demselben T*-Objekt unklar. |
Das Verhalten ist in diesem Fall nicht definiert. |
| LWG 2529 (P0033R1) |
C++11 | Es war unklar, wie das zugrunde liegende std::weak_ptr aktualisiert wird. | wurde klargestellt |
[bearbeiten] Siehe auch
| (C++11) |
intelligenter Zeiger mit semantisch geteiltem Objektbesitz (Klassen-Template) |
| erstellt einen geteilten Zeiger, der ein neues Objekt verwaltet (Funktionsschablone) |