Spec-Zone.ru › C++

std::unique_ptr

Определён в заголовочном файле <memory>
template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;
(1) (начиная с C++11)
template <
    class T,
    class Deleter
> class unique_ptr<T[], Deleter>;
(2) (начиная с C++11)

std::unique_ptr — это умный указатель, который владеет другим объектом (отвечает за него) и управляет им с помощью указателя, а затем удаляет этот объект, когда unique_ptr выходит из области видимости.

Объект удаляется с помощью связанного с ним удалителя при наступлении любого из следующих событий:

  • управляющий объект unique_ptr уничтожается.
  • управляющему объекту unique_ptr присваивается другой указатель с помощью operator= или reset().

Объект удаляется с помощью потенциально заданного пользователем удалителя посредством вызова get_deleter()(ptr). Удалитель по умолчанию (std::default_delete) использует оператор delete, который уничтожает объект и освобождает память.

unique_ptr также может не владеть объектом; в этом случае он называется пустым.

Существует две версии unique_ptr:

  1. Управляет одним объектом (например, выделенным с помощью new).
  2. Управляет динамически выделенным массивом объектов (например, выделенным с помощью new[]).

Класс удовлетворяет требованиям MoveConstructible и MoveAssignable, но не удовлетворяет требованиям CopyConstructible и CopyAssignable.

Если T* не является допустимым типом (например, T является ссылочным типом), программа, в которой инстанцируется определение std::unique_ptr<T,Deleter>, некорректна.

Требования к типам
-Deleter должен быть FunctionObject, ссылкой на lvalue типа FunctionObject или ссылкой на lvalue на функцию, вызываемую с аргументом типа unique_ptr<T,Deleter>::pointer.

Примечания

Только неконстантный unique_ptr может передать владение управляемым объектом другому unique_ptr. Если временем жизни объекта управляет conststd::unique_ptr, оно ограничено областью видимости, в которой был создан указатель.

unique_ptr обычно используется для управления временем жизни объектов, в том числе для:

  • обеспечения безопасности при исключениях в классах и функциях, работающих с объектами с динамическим временем жизни, гарантируя удаление как при обычном выходе, так и при выходе из-за исключения.
  • передачи владения уникально принадлежащими объектами с динамическим временем жизни функциям.
  • получения от функций владения уникально принадлежащими объектами с динамическим временем жизни.
  • использования в качестве типа элементов в контейнерах, поддерживающих перемещение, таких как std::vector, которые содержат указатели на динамически выделенные объекты (например, если требуется полиморфное поведение).

unique_ptr может быть создан для неполного типа T, например, чтобы использовать его в качестве дескриптора в идиоме pImpl. Если используется удалитель по умолчанию, T должен быть полным в точке кода, где вызывается удалитель; это происходит в деструкторе, операторе присваивания перемещением и функции-члене reset класса unique_ptr. (В отличие от этого, std::shared_ptr нельзя создать из сырого указателя на неполный тип, однако его можно уничтожить, когда T является неполным). Обратите внимание: если T является специализацией шаблона класса, использование unique_ptr в качестве операнда, например !p, требует полноты параметров T из-за ADL.

Если T является производным классом некоторого базового класса B, то unique_ptr<T> неявно преобразуется в unique_ptr<B>. Удалитель по умолчанию полученного unique_ptr<B> будет использовать operator delete для B, что приводит к неопределённому поведению, если деструктор B не является виртуальным. Обратите внимание, что std::shared_ptr ведёт себя иначе: std::shared_ptr<B> будет использовать operator delete для типа T, и принадлежащий ему объект будет корректно удалён, даже если деструктор B не является виртуальным.

В отличие от std::shared_ptr, unique_ptr может управлять объектом с помощью любого пользовательского типа дескриптора, удовлетворяющего требованиям NullablePointer. Это позволяет, например, управлять объектами в разделяемой памяти, предоставив Deleter, который определяет typedef boost::offset_ptr pointer; или другой специальный указатель.

Макрос проверки наличия возможности Значение Стандарт Возможность
__cpp_lib_constexpr_memory 202202L (C++23) constexpr std::unique_ptr

Вложенные типы

Тип Определение
pointer std::remove_reference<Deleter>::type::pointer, если такой тип существует; в противном случае T*. Должен удовлетворять требованиям NullablePointer
element_type T, тип объекта, которым управляет этот unique_ptr
deleter_type Deleter, функциональный объект, ссылка на lvalue на функцию или функциональный объект, вызываемый из деструктора

Функции-члены

(constructor)
создаёт новый unique_ptr
(открытая функция-член)
(destructor)
уничтожает управляемый объект, если он существует
(открытая функция-член)
operator=
присваивает unique_ptr
(открытая функция-член)
Модификаторы
release
возвращает указатель на управляемый объект и передаёт владение им
(открытая функция-член)
reset
заменяет управляемый объект
(открытая функция-член)
swap
обменивает управляемые объекты
(открытая функция-член)
Наблюдатели
get
возвращает указатель на управляемый объект
(открытая функция-член)
get_deleter
возвращает удалитель, используемый для уничтожения управляемого объекта
(открытая функция-член)
operator bool
проверяет, связан ли с указателем управляемый объект
(открытая функция-член)
Версия для одного объекта, unique_ptr<T>
operator*operator->
разыменовывает указатель на управляемый объект
(открытая функция-член)
Версия для массива, unique_ptr<T[]>
operator[]
обеспечивает доступ по индексу к управляемому массиву
(открытая функция-член)

Функции, не являющиеся членами

make_uniquemake_unique_for_overwrite
(C++14)(C++20)
создаёт уникальный указатель, управляющий новым объектом
(шаблон функции)
operator==operator!=operator<operator<=operator>operator>=operator<=>
(удалены в C++20)(C++20)
сравнивает с другим unique_ptr или с nullptr
(шаблон функции)
operator<<(std::unique_ptr)
(C++20)
выводит значение управляемого указателя в выходной поток
(шаблон функции)
std::swap(std::unique_ptr)
(C++11)
специализирует алгоритм std::swap
(шаблон функции)

Вспомогательные классы

std::hash<std::unique_ptr>
(C++11)
поддержка хеширования для std::unique_ptr
(специализация шаблона класса)

Пример

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

Возможный вывод:

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

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.

DR Применён к Опубликованное поведение Корректное поведение
LWG 4144 C++11 T* не требовалось образовывать допустимый тип требуется

См. также

shared_ptr
(C++11)
умный указатель с семантикой совместного владения объектом
(шаблон класса)
weak_ptr
(C++11)
слабая ссылка на объект, управляемый std::shared_ptr
(шаблон класса)
indirect
(C++26)
обёртка, содержащая динамически выделенный объект с семантикой, подобной семантике значения
(шаблон класса)
any
(C++17)
объекты, хранящие экземпляры любого типа, удовлетворяющего требованиям CopyConstructible
(класс)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/memory/unique_ptr

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API