Namespaces
Variants
Views
Actions

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

From cppreference.com
< cpp‎ | memoryRedirect page
(Undo revision 158615 by 0xDEADBEEF (talk))
m (Redirected page to enwiki:Rust (programming language))
Line 1: Line 1:
{{cpp/title|unique_ptr}}
+
#REDIRECT [[enwiki:Rust_(programming_language)]]
{{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 and manages another object through a pointer and 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 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 called ''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}}. (Conversely,  {{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}}
+

Revision as of 07:34, 10 September 2023