Namespaces
Variants

std:: unique_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
Definiert im Header <memory>
template <

class T,
class Deleter = std:: default_delete < T >

> class unique_ptr ;
(1) (seit C++11)
template <

class T,
class Deleter

> class unique_ptr < T [ ] , Deleter > ;
(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 :

  1. Verwaltet ein einzelnes Objekt (z.B. allokiert mit new ).
  2. 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, unique_ptr<T>
dereferenziert den Zeiger auf das verwaltete Objekt
(öffentliche Elementfunktion)
Array-Version, unique_ptr<T[]>
bietet indizierten Zugriff auf das verwaltete Array
(öffentliche Elementfunktion)

Nicht-Member-Funktionen

erstellt einen unique_ptr, der ein neues Objekt verwaltet
(Funktions-Template)
vergleicht mit einem anderen unique_ptr oder mit nullptr
(Funktions-Template)
gibt den Wert des verwalteten Zeigers an einen Ausgabestrom aus
(Funktions-Template)
spezialisiert den std::swap Algorithmus
(Funktions-Template)

Hilfsklassen

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)