std:: unique_ptr
|
Definiert im Header
<memory>
|
||
|
template
<
class
T,
|
(1) | (seit C++11) |
|
template
<
class
T,
|
(2) | (seit C++11) |
std::unique_ptr
ist ein intelligenter Zeiger, der ein anderes Objekt über einen Zeiger besitzt (dafür verantwortlich ist) und verwaltet und dieses Objekt anschließend entsorgt, wenn der
unique_ptr
den Gültigkeitsbereich verlässt.
Das Objekt wird unter Verwendung des zugehörigen Löschers entsorgt, wenn eines der folgenden Ereignisse eintritt:
-
Das verwaltende
unique_ptr-Objekt wird zerstört. -
Dem verwaltenden
unique_ptr-Objekt wird über operator= oder reset() ein anderer Zeiger zugewiesen.
Das Objekt wird unter Verwendung eines möglicherweise benutzerdefinierten Löschers entsorgt, indem
get_deleter
(
)
(
ptr
)
aufgerufen wird. Der Standardlöscher (
std::default_delete
) verwendet den
delete
-Operator, der das Objekt zerstört und den Speicher freigibt.
Ein
unique_ptr
kann alternativ kein Objekt besitzen, in welchem Fall er als
empty
bezeichnet wird.
Es gibt zwei Versionen von
unique_ptr
:
- Verwaltet ein einzelnes Objekt (z.B. allokiert mit new ).
- Verwaltet ein dynamisch allokiertes Array von Objekten (z.B. allokiert mit new [ ] ).
Die Klasse erfüllt die Anforderungen von MoveConstructible und MoveAssignable , jedoch weder CopyConstructible noch CopyAssignable .
Wenn
T*
kein gültiger Typ war (z.B., wenn
T
ein Referenztyp ist), ist ein Programm, das die Definition von
std
::
unique_ptr
<
T, Deleter
>
instanziiert, fehlerhaft.
| Typanforderungen | ||
-
Deleter
muss ein
FunctionObject
oder ein Lvalue-Referenz auf einen
FunctionObject
oder eine Lvalue-Referenz auf eine Funktion sein, aufrufbar mit einem Argument vom Typ
unique_ptr
<
T, Deleter
>
::
pointer
.
|
Inhaltsverzeichnis |
Hinweise
Nur nicht-konstante
unique_ptr
können den Besitz des verwalteten Objekts an einen anderen
unique_ptr
übertragen. Wenn die Lebensdauer eines Objekts durch einen
const
std
::
unique_ptr
verwaltet wird, ist sie auf den Gültigkeitsbereich beschränkt, in dem der Zeiger erstellt wurde.
unique_ptr
wird häufig verwendet, um die Lebensdauer von Objekten zu verwalten, einschließlich:
- Bereitstellung von Exception Safety für Klassen und Funktionen, die Objekte mit dynamischer Lebensdauer verwalten, durch Garantie der Löschung bei normalem Beenden und Beenden durch Exception.
- Übergabe des Eigentums an eindeutig besessenen Objekten mit dynamischer Lebensdauer an Funktionen.
- Erwerb des Eigentums an eindeutig besessenen Objekten mit dynamischer Lebensdauer von Funktionen.
- als Elementtyp in bewegungsbewussten Containern, wie std::vector , die Zeiger auf dynamisch allozierte 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 ermöglichen. Wenn der Standard-Deleter verwendet wird, muss
T
an der Stelle im Code vollständig sein, an der der Deleter aufgerufen wird, 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 zerstört werden, wenn
T
unvollständig ist). Beachten Sie, dass wenn
T
eine Klassentemplatespezialisierung ist, die Verwendung von
unique_ptr
als Operand, z.B.
!
p
, die Vollständigkeit von
T
's Parametern aufgrund von
ADL
erfordert.
Wenn
T
eine
abgeleitete Klasse
einer Basisklasse
B
ist, dann ist
unique_ptr
<
T
>
implizit konvertierbar
zu
unique_ptr
<
B
>
. Der Standard-Löscher 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
virtual
. Beachten Sie, dass sich
std::shared_ptr
anders verhält:
std::
shared_ptr
<
B
>
verwendet
operator delete
für den Typ
T
und das verwaltete Objekt wird korrekt gelöscht, selbst wenn der Destruktor von
B
nicht
virtual
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 in Shared Memory, 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
|
Verschachtelte Typen
| Typ | Definition |
| pointer |
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 Funktionsobjekt oder die Lvalue-Referenz auf eine Funktion oder ein Funktionsobjekt, das vom Destruktor aufgerufen wird
|
Memberfunktionen
konstruiert einen neuen
unique_ptr
(öffentliche Elementfunktion) |
|
|
zerstört das verwaltete Objekt, falls vorhanden
(öffentliche Elementfunktion) |
|
weist den
unique_ptr
zu
(öffentliche Elementfunktion) |
|
Modifikatoren |
|
|
gibt einen Zeiger auf das verwaltete Objekt zurück und gibt die Eigentümerschaft frei
(öffentliche Elementfunktion) |
|
|
ersetzt das verwaltete Objekt
(öffentliche Elementfunktion) |
|
|
tauscht die verwalteten Objekte
(öffentliche Elementfunktion) |
|
Beobachter |
|
|
gibt einen Zeiger auf das verwaltete Objekt zurück
(öffentliche Elementfunktion) |
|
|
gibt den Deleter zurück, der für die Zerstörung des verwalteten Objekts verwendet wird
(öffentliche Elementfunktion) |
|
|
prüft, ob ein assoziiertes verwaltetes Objekt vorhanden ist
(öffentliche Elementfunktion) |
|
Einzelobjekt-Version,
|
|
|
dereferenziert den Zeiger auf das verwaltete Objekt
(öffentliche Elementfunktion) |
|
Array-Version,
|
|
|
bietet indizierten Zugriff auf das verwaltete Array
(öffentliche Elementfunktion) |
|
Nicht-Member-Funktionen
|
(C++14)
(C++20)
|
erstellt einen unique_ptr, der ein neues Objekt verwaltet
(Funktions-Template) |
|
(entfernt in C++20)
(C++20)
|
vergleicht mit einem anderen
unique_ptr
oder mit
nullptr
(Funktions-Template) |
|
(C++20)
|
gibt den Wert des verwalteten Zeigers an einen Ausgabestrom aus
(Funktions-Template) |
|
(C++11)
|
spezialisiert den
std::swap
Algorithmus
(Funktions-Template) |
Hilfsklassen
|
(C++11)
|
Hash-Unterstützung für
std::unique_ptr
(Klassen-Template-Spezialisierung) |
Beispiel
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // Hilfsklasse für Runtime-Polymorphismus-Demo unten 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"; } }; // eine Funktion, die einen unique_ptr konsumiert, kann ihn als Wert oder als Rvalue-Referenz übernehmen std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // Hilfsfunktion für die benutzerdefinierte Deleter-Demo unten void close_file(std::FILE* fp) { std::fclose(fp); } // unique_ptr-basierte Linked-List-Demo struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // Listenelemente sequenziell in einer Schleife zerstören, der Standard-Destruktor // hätte seinen "next"-Destruktor rekursiv aufgerufen, was // Stack Overflow für ausreichend große Listen verursachen. 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) Demonstration der Unique-Ownership-Semantik\n"; { // Erstellen einer (eindeutig besessenen) Ressource std::unique_ptr<D> p = std::make_unique<D>(); // Übertrage Eigentum an "pass_through", // was wiederum den Besitz durch den Rückgabewert zurücküberträgt std::unique_ptr<D> q = pass_through(std::move(p)); // „p“ befindet sich nun in einem verschobenen 'leeren' Zustand, gleich nullptr assert(!p); } std::cout << "\n" "2) Laufzeitpolymorphismus-Demo\n"; { // Erstellen Sie eine abgeleitete Ressource und zeigen Sie über den Basistyp darauf std::unique_ptr<B> p = std::make_unique<D>(); // Dynamic Dispatch funktioniert wie erwartet p->bar(); } std::cout << "\n" "3) Benutzerdefinierter Löscher-Demo\n"; std::ofstream("demo.txt") << 'x'; // Datei zum Lesen vorbereiten { 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()” wird hier aufgerufen (falls “fp” nicht null ist) std::cout << "\n" "4) Benutzerdefinierter Lambda-Ausdruck-Löscher und Exception-Safety-Demo\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) { std::cout << "Zerstören über einen benutzerdefinierten Deleter...\n"; delete ptr; }); throw std::runtime_error(""); // “p” würde hier ein Speicherleck verursachen, wenn es ein einfacher Zeiger wäre } catch (const std::exception&) { std::cout << "Caught exception\n"; } std::cout << "\n" "5) Array-Form von unique_ptr Demo\n"; { std::unique_ptr<D[]> p(new D[3]); } // “D::~D()” wird 3 Mal aufgerufen std::cout << "\n" "6) Verkettete Liste 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 << " Flaschen Bier an der Wand...\n"; } // zerstört alle Biere }
Mögliche Ausgabe:
1) Demo der Unique-Ownership-Semantik D::D D::bar D::~D 2) Demo der Laufzeitpolymorphie D::D D::bar D::~D 3) Demo des benutzerdefinierten Deleter x 4) Demo des benutzerdefinierten Lambda-Ausdruck-Deleters und Ausnahmesicherheit D::D Zerstörung durch einen benutzerdefinierten Deleter... D::~D Ausnahme abgefangen 5) Demo der Array-Form von unique_ptr D::D D::D D::D D::~D D::~D D::~D 6) Demo der verketteten Liste 1.000.000 Flaschen Bier an der Wand...
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| LWG 4144 | C++11 |
T*
war nicht erforderlich, um einen gültigen Typ zu bilden
|
erforderlich |
Siehe auch
|
(C++11)
|
Intelligenter Zeiger mit Shared-Ownership-Semantik
(Klassentemplate) |
|
(C++11)
|
Schwache Referenz auf ein von
std::shared_ptr
verwaltetes Objekt
(Klassentemplate) |
|
(C++26)
|
Ein Wrapper für dynamisch allokierte Objekte mit wertähnlicher Semantik
(Klassentemplate) |
|
(C++17)
|
Objekte, die Instanzen beliebiger
CopyConstructible
Typen halten
(Klasse) |