Namensräume
Varianten
Aktionen

std::unique_ptr

Von cppreference.com
< cpp‎ | memory
 
 
Speicherverwaltungsbibliothek
(nur Exposition*)
Algorithmen für uninitialisierten Speicher
(C++17)
(C++17)
(C++17)
Beschränkte uninitialisierte
Speicher-Algorithmen
C-Bibliothek

Allocatoren
Speicherressourcen
Unterstützung für Garbage Collection
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
Uninitialisierter Speicher
Explizites Lebenszeitmanagement
 
 
Definiert in 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 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:

  1. Verwaltet ein einzelnes Objekt (z. B. alloziert mit new).
  2. 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) [bearbeiten]
Zerstört das verwaltete Objekt, falls vorhanden
(öffentliche Memberfunktion) [bearbeiten]
Weist den unique_ptr zu
(öffentliche Memberfunktion) [bearbeiten]
Modifizierer
Gibt einen Zeiger auf das verwaltete Objekt zurück und gibt die Eigentümerschaft frei
(öffentliche Memberfunktion) [bearbeiten]
Ersetzt das verwaltete Objekt
(öffentliche Memberfunktion) [bearbeiten]
Vertauscht die verwalteten Objekte
(öffentliche Memberfunktion) [bearbeiten]
Observer
Gibt einen Zeiger auf das verwaltete Objekt zurück
(öffentliche Memberfunktion) [bearbeiten]
Gibt den Deleter zurück, der zur Zerstörung des verwalteten Objekts verwendet wird
(öffentliche Memberfunktion) [bearbeiten]
Prüft, ob ein zugehöriges verwaltetes Objekt vorhanden ist
(öffentliche Memberfunktion) [bearbeiten]
Einzelobjekt-Version, unique_ptr<T>
Dereferenziert den Zeiger auf das verwaltete Objekt
(öffentliche Memberfunktion) [bearbeiten]
Array-Version, unique_ptr<T[]>
Bietet indizierten Zugriff auf das verwaltete Array
(öffentliche Memberfunktion) [bearbeiten]

[bearbeiten] Nicht-Member-Funktionen

Erstellt einen unique Pointer, der ein neues Objekt verwaltet
(Funktionsschablone) [bearbeiten]
Vergleicht mit einem anderen unique_ptr oder mit nullptr
(Funktionsschablone) [bearbeiten]
Gibt den Wert des verwalteten Zeigers an einen Ausgabestrom aus
(Funktionsschablone) [bearbeiten]
spezialisiert den Algorithmus std::swap
(Funktionsschablone) [bearbeiten]

[bearbeiten] Hilfsklassen

Hash-Unterstützung für std::unique_ptr
(Klassentemplate-Spezialisierung) [bearbeiten]

[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

intelligenter Zeiger mit semantisch geteiltem Objektbesitz
(Klassen-Template) [edit]
(C++11)
schwache Referenz auf ein von std::shared_ptr verwaltetes Objekt
(Klassen-Template) [edit]