Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/memory/unique ptr"

From cppreference.com
< cpp‎ | memory
m (Redirected page to enwiki:Rust (programming language))
m (Replace misused word)
 
(15 intermediate revisions by 9 users not shown)
Line 1: Line 1:
#REDIRECT [[enwiki:Rust_(programming_language)]]
+
{{cpp/title|unique_ptr}}
 +
{{cpp/memory/unique_ptr/navbar}}
 +
{{dcl begin}}
 +
{{dcl header|memory}}
 +
{{dcl|since=c++11|num=1|1=
 +
template<
 +
    class T,
 +
    class Deleter = std::default_delete<T>
 +
> class unique_ptr;
 +
}}
 +
{{dcl|since=c++11|num=2|1=
 +
template <
 +
    class T,
 +
    class Deleter
 +
> class unique_ptr<T[], Deleter>;
 +
}}
 +
{{dcl end}}
 +
 
 +
{{tt|std::unique_ptr}} is a smart pointer that owns (is responsible for) and manages another object via a pointer and subsequently disposes of that object when the {{tt|unique_ptr}} goes out of scope.
 +
 
 +
The object is disposed of, using the associated deleter, when either of the following happens:
 +
* the managing {{tt|unique_ptr}} object is destroyed.
 +
* the managing {{tt|unique_ptr}} object is assigned another pointer via {{lc|1=operator=}} or {{lc|reset()}}.
 +
 
 +
The object is disposed of, using a potentially user-supplied deleter, by calling {{c|get_deleter()(ptr)}}. The default deleter ({{tt|std::default_delete}}) uses the {{c|delete}} operator, which destroys the object and deallocates the memory.
 +
 
 +
A {{tt|unique_ptr}} may alternatively own no object, in which case it is described as ''empty''.
 +
 
 +
There are two versions of {{tt|std::unique_ptr}}:
 +
# Manages a single object (e.g., allocated with {{c|new}}).
 +
# Manages a dynamically-allocated array of objects (e.g., allocated with {{c|new[]}}).
 +
 
 +
The class satisfies the requirements of {{named req|MoveConstructible}} and {{named req|MoveAssignable}}, but of neither {{named req|CopyConstructible}} nor {{named req|CopyAssignable}}.
 +
 
 +
{{par begin}}
 +
{{par hreq}}
 +
{{par req|{{tt|Deleter}} must be {{named req|FunctionObject}} or lvalue reference to a {{named req|FunctionObject}} or lvalue reference to function, callable with an argument of type {{c|unique_ptr<T, Deleter>::pointer}}.}}
 +
{{par end}}
 +
 
 +
===Notes===
 +
Only non-const {{tt|unique_ptr}} can transfer the ownership of the managed object to another {{tt|unique_ptr}}. If an object's lifetime is managed by a {{c|const std::unique_ptr}}, it is limited to the scope in which the pointer was created.
 +
 
 +
{{tt|std::unique_ptr}} is commonly used to manage the lifetime of objects, including:
 +
 
 +
* providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception.
 +
 
 +
* passing ownership of uniquely-owned objects with dynamic lifetime into functions.
 +
 
 +
* acquiring ownership of uniquely-owned objects with dynamic lifetime from functions.
 +
 
 +
* as the element type in move-aware containers, such as {{lc|std::vector}}, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired).
 +
 
 +
{{tt|std::unique_ptr}} may be constructed for an {{lt|cpp/language/incomplete type}} {{tt|T}}, such as to facilitate the use as a handle in the [[cpp/language/pimpl|pImpl idiom]]. If the default deleter is used, {{tt|T}} must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and {{tt|reset}} member function of {{tt|std::unique_ptr}}. (In contrast,  {{lc|std::shared_ptr}} can't be constructed from a raw pointer to incomplete type, but can be destroyed where {{tt|T}} is incomplete). Note that if {{tt|T}} is a class template specialization, use of {{tt|unique_ptr}} as an operand, e.g. {{c|!p}} requires {{tt|T}}'s parameters to be complete due to [[cpp/language/adl|ADL]].
 +
 
 +
If {{tt|T}} is a [[cpp/language/derived_class|derived class]] of some base {{tt|B}}, then {{c|std::unique_ptr<T>}} is [[cpp/memory/unique_ptr/unique_ptr|implicitly convertible]] to {{c|std::unique_ptr<B>}}. The default deleter of the resulting {{c|std::unique_ptr<B>}} will use [[cpp/memory/new/operator_delete|operator delete]] for {{tt|B}}, leading to [[cpp/language/destructor#Virtual_destructors|undefined behavior]] unless the destructor of {{tt|B}} is [[cpp/language/virtual|virtual]]. Note that {{lc|std::shared_ptr}} behaves differently: {{c|std::shared_ptr<B>}} will use the [[cpp/memory/new/operator_delete|operator delete]] for the type {{tt|T}} and the owned object will be deleted correctly even if the destructor of {{tt|B}} is not [[cpp/language/virtual|virtual]].
 +
 
 +
Unlike {{lc|std::shared_ptr}}, {{tt|std::unique_ptr}} may manage an object through any custom handle type that satisfies {{named req|NullablePointer}}. This allows, for example, managing objects located in shared memory, by supplying a {{tt|Deleter}} that defines {{tt|typedef [https://www.boost.org/doc/libs/release/doc/html/boost/interprocess/offset_ptr.html boost::offset_ptr] pointer;}} or another [[cpp/named req/Allocator#Fancy_pointers|fancy pointer]].
 +
 
 +
{{feature test macro|__cpp_lib_constexpr_memory|{{co|constexpr}} {{ttt|std::unique_ptr}}|value=202202L|std=C++23}}
 +
 
 +
===Member types===
 +
{{dsc begin}}
 +
{{dsc hitem|Member type|Definition}}
 +
{{dsc|{{co|pointer}}|{{c|std::remove_reference<Deleter>::type::pointer}} if that type exists, otherwise {{tt|T*}}. Must satisfy {{named req|NullablePointer}}}}
 +
{{dsc|{{co|element_type}}|{{tt|T}}, the type of the object managed by this {{tt|unique_ptr}}}}
 +
{{dsc|{{co|deleter_type}}|{{tt|Deleter}}, the function object or lvalue reference to function or to function object, to be called from the destructor}}
 +
{{dsc end}}
 +
 
 +
===Member functions===
 +
{{dsc begin}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc constructor}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc destructor}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator{{=}}}}
 +
 
 +
{{dsc h2|Modifiers}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc release}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc reset}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc swap}}
 +
 
 +
{{dsc h2|Observers}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc get}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc get_deleter}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator bool}}
 +
 
 +
{{dsc h2|Single-object version, {{tt|unique_ptr<T>}}}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator*}}
 +
 
 +
{{dsc h2|Array version, {{tt|unique_ptr<T[]>}}}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator_at}}
 +
 
 +
{{dsc end}}
 +
 
 +
===Non-member functions===
 +
{{dsc begin}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc make_unique}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator_cmp}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc operator_ltlt}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc swap2}}
 +
{{dsc end}}
 +
 
 +
===Helper classes===
 +
{{dsc begin}}
 +
{{dsc inc|cpp/memory/unique_ptr/dsc hash}}
 +
{{dsc end}}
 +
 
 +
===Example===
 +
{{example
 +
|code=
 +
#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
 +
}
 +
|p=true
 +
|output=
 +
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...
 +
}}
 +
 
 +
===See also===
 +
{{dsc begin}}
 +
{{dsc inc|cpp/memory/dsc shared_ptr}}
 +
{{dsc inc|cpp/memory/dsc weak_ptr}}
 +
{{dsc end}}
 +
 
 +
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}

Latest revision as of 22:28, 30 September 2024

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
Dynamic memory management
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Allocators
Garbage collection support
(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)



 
 
Defined in header <memory>
template<

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

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

    class T,
    class Deleter

> class unique_ptr<T[], Deleter>;
(2) (since C++11)

std::unique_ptr is a smart pointer that owns (is responsible for) and manages another object via a pointer and subsequently disposes of that object when the unique_ptr goes out of scope.

The object is disposed of, using the associated deleter, when either of the following happens:

  • the managing unique_ptr object is destroyed.
  • the managing unique_ptr object is assigned another pointer via operator= or reset().

The object is disposed of, using a potentially user-supplied deleter, by calling get_deleter()(ptr). The default deleter (std::default_delete) uses the delete operator, which destroys the object and deallocates the memory.

A unique_ptr may alternatively own no object, in which case it is described as empty.

There are two versions of std::unique_ptr:

  1. Manages a single object (e.g., allocated with new).
  2. Manages a dynamically-allocated array of objects (e.g., allocated with new[]).

The class satisfies the requirements of MoveConstructible and MoveAssignable, but of neither CopyConstructible nor CopyAssignable.

Type requirements
-
Deleter must be FunctionObject or lvalue reference to a FunctionObject or lvalue reference to function, callable with an argument of type unique_ptr<T, Deleter>::pointer.

Contents

[edit] Notes

Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. If an object's lifetime is managed by a const std::unique_ptr, it is limited to the scope in which the pointer was created.

std::unique_ptr is commonly used to manage the lifetime of objects, including:

  • providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception.
  • passing ownership of uniquely-owned objects with dynamic lifetime into functions.
  • acquiring ownership of uniquely-owned objects with dynamic lifetime from functions.
  • as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired).

std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the pImpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr. (In contrast, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete). Note that if T is a class template specialization, use of unique_ptr as an operand, e.g. !p requires T's parameters to be complete due to ADL.

If T is a derived class of some base B, then std::unique_ptr<T> is implicitly convertible to std::unique_ptr<B>. The default deleter of the resulting std::unique_ptr<B> will use operator delete for B, leading to undefined behavior unless the destructor of B is virtual. Note that std::shared_ptr behaves differently: std::shared_ptr<B> will use the operator delete for the type T and the owned object will be deleted correctly even if the destructor of B is not virtual.

Unlike std::shared_ptr, std::unique_ptr may manage an object through any custom handle type that satisfies NullablePointer. This allows, for example, managing objects located in shared memory, by supplying a Deleter that defines typedef boost::offset_ptr pointer; or another fancy pointer.

Feature-test macro Value Std Feature
__cpp_lib_constexpr_memory 202202L (C++23) constexpr std::unique_ptr

[edit] Member types

Member type Definition
pointer std::remove_reference<Deleter>::type::pointer if that type exists, otherwise T*. Must satisfy NullablePointer
element_type T, the type of the object managed by this unique_ptr
deleter_type Deleter, the function object or lvalue reference to function or to function object, to be called from the destructor

[edit] Member functions

constructs a new unique_ptr
(public member function) [edit]
destructs the managed object if such is present
(public member function) [edit]
assigns the unique_ptr
(public member function) [edit]
Modifiers
returns a pointer to the managed object and releases the ownership
(public member function) [edit]
replaces the managed object
(public member function) [edit]
swaps the managed objects
(public member function) [edit]
Observers
returns a pointer to the managed object
(public member function) [edit]
returns the deleter that is used for destruction of the managed object
(public member function) [edit]
checks if there is an associated managed object
(public member function) [edit]
Single-object version, unique_ptr<T>
dereferences pointer to the managed object
(public member function) [edit]
Array version, unique_ptr<T[]>
provides indexed access to the managed array
(public member function) [edit]

[edit] Non-member functions

creates a unique pointer that manages a new object
(function template) [edit]
compares to another unique_ptr or with nullptr
(function template) [edit]
outputs the value of the managed pointer to an output stream
(function template) [edit]
specializes the std::swap algorithm
(function template) [edit]

[edit] Helper classes

hash support for std::unique_ptr
(class template specialization) [edit]

[edit] Example

#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
}

Possible output:

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...

[edit] See also

smart pointer with shared object ownership semantics
(class template) [edit]
(C++11)
weak reference to an object managed by std::shared_ptr
(class template) [edit]