std::unique_ptr
| Definiert in Header <memory> |
||
| template< class T, |
(1) | (seit C++11) |
| template < class T, |
(2) | (seit C++11) |
std::unique_ptr ist ein Smart Pointer, der ein anderes Objekt über einen Zeiger verwaltet (dafür verantwortlich ist) und dieses Objekt zerstört, wenn der unique_ptr seinen Gültigkeitsbereich verlässt.
Das Objekt wird unter Verwendung des zugehörigen Deleters zerstört, wenn eine der folgenden Bedingungen eintritt:
- Das verwaltende
unique_ptr-Objekt wird zerstört. - Das verwaltende
unique_ptr-Objekt wird über operator= oder reset() einem anderen Zeiger zugewiesen.
Das Objekt wird unter Verwendung eines potenziell benutzerspezifizierten Deleters zerstört, indem get_deleter()(ptr) aufgerufen wird. Der Standard-Deleter (std::default_delete) verwendet den delete-Operator, der das Objekt zerstört und den Speicher freigibt.
Ein unique_ptr kann alternativ kein Objekt verwalten, in welchem Fall er als leer bezeichnet wird.
Es gibt zwei Versionen von unique_ptr:
- Verwaltet ein einzelnes Objekt (z. B. alloziert mit new).
- Verwaltet ein dynamisch alloziertes Array von Objekten (z. B. alloziert mit new[]).
Die Klasse erfüllt die Anforderungen an MoveConstructible und MoveAssignable, jedoch weder an CopyConstructible noch CopyAssignable.
Wenn T* kein gültiger Typ war (z. B. T ist ein Referenztyp), ist ein Programm, das die Definition von std::unique_ptr<T, Deleter> instanziiert, ill-formed.
| Typanforderungen | ||
-Deleter muss ein FunctionObject oder eine lvalue-Referenz auf ein FunctionObject oder eine lvalue-Referenz auf eine Funktion sein, die mit einem Argument vom Typ unique_ptr<T, Deleter>::pointer aufrufbar ist. |
Inhalt |
[bearbeiten] Anmerkungen
Nur non-const unique_ptr können die Eigentümerschaft des verwalteten Objekts auf einen anderen unique_ptr übertragen. Wenn die Lebensdauer eines Objekts von einem const std::unique_ptr verwaltet wird, ist diese auf den Gültigkeitsbereich beschränkt, in dem der Zeiger erstellt wurde.
unique_ptr wird häufig zur Verwaltung der Lebensdauer von Objekten verwendet, einschließlich:
- Bereitstellung von Exception-Sicherheit für Klassen und Funktionen, die Objekte mit dynamischer Lebensdauer behandeln, indem die Zerstörung sowohl bei normalem Beenden als auch bei Ausnahmen garantiert wird.
- Übertragung der Eigentümerschaft von eindeutig besessenen Objekten mit dynamischer Lebensdauer in Funktionen.
- Erwerb der Eigentümerschaft von eindeutig besessenen Objekten mit dynamischer Lebensdauer aus Funktionen.
- Als Elementtyp in Move-fähigen Containern wie std::vector, die Zeiger auf dynamisch alloziierte Objekte halten (z. B. wenn polymorphes Verhalten gewünscht ist).
unique_ptr kann für einen unvollständigen Typ T konstruiert werden, um beispielsweise die Verwendung als Handle im pImpl-Idiom zu erleichtern. Wenn der Standard-Deleter verwendet wird, muss T an der Stelle im Code, an der der Deleter aufgerufen wird, vollständig sein, was im Destruktor, im Move-Zuweisungsoperator und in der reset-Memberfunktion von unique_ptr geschieht. (Im Gegensatz dazu kann std::shared_ptr nicht aus einem Rohzeiger auf einen unvollständigen Typ konstruiert werden, kann aber dort zerstört werden, wo T unvollständig ist). Beachten Sie, dass bei Verwendung von unique_ptr als Operand, z. B. !p, die Parameter von T aufgrund von ADL vollständig sein müssen, wenn T eine Klassenschablone ist.
Wenn T eine abgeleitete Klasse einer Basisklasse B ist, dann ist unique_ptr<T> implizit konvertierbar zu unique_ptr<B>. Der Standard-Deleter des resultierenden unique_ptr<B> verwendet operator delete für B, was zu undefiniertem Verhalten führt, es sei denn, der Destruktor von B ist virtuell. Beachten Sie, dass std::shared_ptr sich anders verhält: std::shared_ptr<B> verwendet operator delete für den Typ T und das verwaltete Objekt wird korrekt gelöscht, auch wenn der Destruktor von B nicht virtuell ist.
Im Gegensatz zu std::shared_ptr kann unique_ptr ein Objekt über jeden benutzerdefinierten Handle-Typ verwalten, der NullablePointer erfüllt. Dies ermöglicht beispielsweise die Verwaltung von Objekten, die sich im Shared Memory befinden, indem ein Deleter bereitgestellt wird, der typedef boost::offset_ptr pointer; oder einen anderen Fancy Pointer definiert.
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_lib_constexpr_memory |
202202L |
(C++23) | constexpr std::unique_ptr |
[bearbeiten] Verschachtelte Typen
| Typ | Definition |
| Zeiger | std::remove_reference<Deleter>::type::pointer, falls dieser Typ existiert, andernfalls T*. Muss NullablePointer erfüllen. |
| element_type | T, der Typ des von diesem unique_ptr verwalteten Objekts |
| deleter_type | Deleter, das Funktions-Objekt oder die lvalue-Referenz auf eine Funktion oder ein Funktions-Objekt, das im Destruktor aufgerufen wird |
[bearbeiten] Memberfunktionen
Konstruiert einen neuen unique_ptr(öffentliche Memberfunktion) | |
| Zerstört das verwaltete Objekt, falls vorhanden (öffentliche Memberfunktion) | |
Weist den unique_ptr zu(öffentliche Memberfunktion) | |
Modifizierer | |
| Gibt einen Zeiger auf das verwaltete Objekt zurück und gibt die Eigentümerschaft frei (öffentliche Memberfunktion) | |
| Ersetzt das verwaltete Objekt (öffentliche Memberfunktion) | |
| Vertauscht die verwalteten Objekte (öffentliche Memberfunktion) | |
Observer | |
| Gibt einen Zeiger auf das verwaltete Objekt zurück (öffentliche Memberfunktion) | |
| Gibt den Deleter zurück, der zur Zerstörung des verwalteten Objekts verwendet wird (öffentliche Memberfunktion) | |
| Prüft, ob ein zugehöriges verwaltetes Objekt vorhanden ist (öffentliche Memberfunktion) | |
Einzelobjekt-Version,
| |
| Dereferenziert den Zeiger auf das verwaltete Objekt (öffentliche Memberfunktion) | |
Array-Version,
| |
| Bietet indizierten Zugriff auf das verwaltete Array (öffentliche Memberfunktion) | |
[bearbeiten] Nicht-Member-Funktionen
| (C++14)(C++20) |
Erstellt einen unique Pointer, der ein neues Objekt verwaltet (Funktionsschablone) |
| (entfernt in C++20)(C++20) |
Vergleicht mit einem anderen unique_ptr oder mit nullptr(Funktionsschablone) |
| (C++20) |
Gibt den Wert des verwalteten Zeigers an einen Ausgabestrom aus (Funktionsschablone) |
| (C++11) |
spezialisiert den Algorithmus std::swap (Funktionsschablone) |
[bearbeiten] Hilfsklassen
| (C++11) |
Hash-Unterstützung für std::unique_ptr (Klassentemplate-Spezialisierung) |
[bearbeiten] Beispiel
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // helper class for runtime polymorphism demo below struct B { virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; } }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // a function consuming a unique_ptr can take it by value or by rvalue reference std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // helper function for the custom deleter demo below void close_file(std::FILE* fp) { std::fclose(fp); } // unique_ptr-based linked list demo struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // destroy list nodes sequentially in a loop, the default destructor // would have invoked its “next”'s destructor recursively, which would // cause stack overflow for sufficiently large lists. while (head) { auto next = std::move(head->next); head = std::move(next); } } void push(int data) { head = std::unique_ptr<Node>(new Node{data, std::move(head)}); } }; int main() { std::cout << "1) Unique ownership semantics demo\n"; { // Create a (uniquely owned) resource std::unique_ptr<D> p = std::make_unique<D>(); // Transfer ownership to “pass_through”, // which in turn transfers ownership back through the return value std::unique_ptr<D> q = pass_through(std::move(p)); // “p” is now in a moved-from 'empty' state, equal to nullptr assert(!p); } std::cout << "\n" "2) Runtime polymorphism demo\n"; { // Create a derived resource and point to it via base type std::unique_ptr<B> p = std::make_unique<D>(); // Dynamic dispatch works as expected p->bar(); } std::cout << "\n" "3) Custom deleter demo\n"; std::ofstream("demo.txt") << 'x'; // prepare the file to read { using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; unique_file_t fp(std::fopen("demo.txt", "r"), &close_file); if (fp) std::cout << char(std::fgetc(fp.get())) << '\n'; } // “close_file()” called here (if “fp” is not null) std::cout << "\n" "4) Custom lambda expression deleter and exception safety demo\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) { std::cout << "destroying from a custom deleter...\n"; delete ptr; }); throw std::runtime_error(""); // “p” would leak here if it were a plain pointer } catch (const std::exception&) { std::cout << "Caught exception\n"; } std::cout << "\n" "5) Array form of unique_ptr demo\n"; { std::unique_ptr<D[]> p(new D[3]); } // “D::~D()” is called 3 times std::cout << "\n" "6) Linked list demo\n"; { List wall; const int enough{1'000'000}; for (int beer = 0; beer != enough; ++beer) wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8")); std::cout << enough << " bottles of beer on the wall...\n"; } // destroys all the beers }
Mögliche Ausgabe
1) Unique ownership semantics demo D::D D::bar D::~D 2) Runtime polymorphism demo D::D D::bar D::~D 3) Custom deleter demo x 4) Custom lambda-expression deleter and exception safety demo D::D destroying from a custom deleter... D::~D Caught exception 5) Array form of unique_ptr demo D::D D::D D::D D::~D D::~D D::~D 6) Linked list demo 1,000,000 bottles of beer on the wall...
[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 4144 | C++11 | T* musste nicht zwingend einen gültigen Typ bilden |
Gefordert |
[bearbeiten] Siehe auch
| (C++11) |
intelligenter Zeiger mit semantisch geteiltem Objektbesitz (Klassen-Template) |
| (C++11) |
schwache Referenz auf ein von std::shared_ptr verwaltetes Objekt (Klassen-Template) |