Пространства имён
Варианты
Действия

std::unique_ptr

Материал из cppreference.com
< cpp‎ | memory
 
 
Библиотека утилит
Языковая поддержка
Поддержка типов (базовые типы, RTTI)
Макросы тестирования функциональности библиотеки (C++20)    
Управление динамической памятью
Программные утилиты
Поддержка сопрограмм (C++20)
Вариативные функции
Трёхстороннее сравнение (C++20)
(C++20)
(C++20)(C++20)(C++20)(C++20)(C++20)(C++20)
Общие утилиты
Дата и время
Функциональные объекты
Библиотека форматирования (C++20)
(C++11)
Операторы отношения (устарело в C++20)
Целочисленные функции сравнения
(C++20)(C++20)(C++20)    
(C++20)
Операции обмена и типа
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
Общие лексические типы
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
Элементарные преобразования строк
(C++17)
(C++17)
 
Динамическое управление памятью
no section name
Ограниченные алгоритмы неинициализированной памяти
no section name
Поддержка сбора мусора
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)



no section name
 
 
Определено в заголовочном файле <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). По умолчанию средство удаления использует оператор delete, который уничтожает объект и освобождает память.

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

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

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

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

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

Содержание

[править] Примечания

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

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

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

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

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

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

Макрос Тестирования функциональности Значение Стандарт Функциональность
__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, функциональный объект или левосторонняя ссылка на функцию или функциональный объект, вызываемые из деструктора

[править] Функции элементы

создаёт новый unique_ptr
(public функция-элемент) [править]
разрушает управляемый объект, если таковой присутствует
(public функция-элемент) [править]
присваивает unique_ptr
(public функция-элемент) [править]
Модификаторы
возвращает указатель на управляемый объект и освобождает владение
(public функция-элемент) [править]
заменяет управляемый объект
(public функция-элемент) [править]
обменивает управляемые объекты
(public функция-элемент) [править]
Наблюдатели
возвращает указатель на управляемый объект
(public функция-элемент) [править]
возвращает средство удаления, которое используется для уничтожения управляемого объекта
(public функция-элемент) [править]
проверяет, есть ли связанный управляемый объект
(public функция-элемент) [править]
Однообъектная версия, unique_ptr<T>
разыменовывает указатель на управляемый объект
(public функция-элемент) [править]
Версия массива, unique_ptr<T[]>
обеспечивает индексированный доступ к управляемому массиву
(public функция-элемент) [править]

[править] Функции, не являющиеся элементами

создаёт уникальный указатель, который управляет новым объектом
(шаблон функции) [править]
сравнивает с другим unique_ptr или с nullptr
(шаблон функции) [править]
выводит значение управляемого указателя в выходной поток
(шаблон функции) [править]
специализация алгоритма std::swap
(шаблон функции) [править]

[править] Вспомогательные классы

поддержка хэширования для std::unique_ptr
(специализация шаблона класса) [править]

[править] Пример

#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <stdexcept>
 
// вспомогательный класс для демонстрации полиморфизма времени выполнения ниже
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";  }
};
 
// функция, принимающая unique_ptr, может принимать его по значению или по правосторонней
// ссылке
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
    p->bar();
    return p;
}
 
// вспомогательная функция для демонстрации пользовательского удаления ниже
void close_file(std::FILE* fp)
{ 
    std::fclose(fp);
}
 
// Демонстрация связанного списка на основе unique_ptr
struct List
{
    struct Node
    {
        int data;
        std::unique_ptr<Node> next;
    };
 
    std::unique_ptr<Node> head;
 
    ~List()
    {
        // уничтожение узлов списка последовательно в цикле, деструктор по умолчанию
        // рекурсивно вызывал бы свой `следующий` деструктор, что вызвало бы
        // переполнение стека для достаточно больших списков.
        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) Демонстрация уникальной семантики владения\n";
    {
        // Создаётся ресурс (с уникальным владением)
        std::unique_ptr<D> p = std::make_unique<D>();
 
        // Передача права собственности в `pass_through`, которая, в свою очередь,
        // передаёт право собственности обратно через возвращаемое значение
        std::unique_ptr<D> q = pass_through(std::move(p));
 
        // `p` теперь находится в перемещённом 'пустом' состоянии, равном `nullptr`
        assert(!p);
    }
 
    std::cout << "\n" "2) Демонстрация полиморфизма времени выполнения\n";
    {
        // Создаёт производный ресурс и указывает на него через базовый тип
        std::unique_ptr<B> p = std::make_unique<D>();
 
        // Динамическая диспетчеризация работает должным образом
        p->bar();
    }
 
    std::cout << "\n" "3) Демонстрация пользовательского удаления\n";
    std::ofstream("demo.txt") << 'x'; // подготовка файла для чтения
    {
        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';
    } // здесь вызывается `fclose()` (если `fp` не null)
 
    std::cout << "\n" "4) Демонстрация пользовательского удаления лямбда-выражением"
                 "и безопасности исключений\n";
    try
    {
        std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
        {
            std::cout << "уничтожение с помощью пользовательского средства удаления...\n";
            delete ptr;
        });
 
        throw std::runtime_error(""); // `p` повис бы здесь, если бы вместо этого
                                      // был бы простой указатель
    }
    catch (const std::exception&) { std::cout << "Поймано исключение\n"; }
 
    std::cout << "\n" "5) Демонстрация формы массива unique_ptr\n";
    {
        std::unique_ptr<D[]> p(new D[3]);
    } // `D::~D()` вызывается 3 раза
 
    std::cout << "\n" "6) Демонстрация связанного списка\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 << " бутылок пива на стойке...\n";
    } // уничтожает всё пиво
}

Вывод:

1) Демонстрация уникальной семантики владения
D::D
D::bar
D::~D
 
2) Демонстрация полиморфизма времени выполнения
D::D
D::bar
D::~D
 
3) Демонстрация пользовательского удаления
x
 
4) Демонстрация пользовательского удаления лямбда-выражением и безопасности исключений
D::D
уничтожение с помощью пользовательского средства удаления...
D::~D
Поймано исключение
 
5) Демонстрация формы массива unique_ptr
D::D
D::D
D::D
D::~D
D::~D
D::~D
 
6) Демонстрация связанного списка
1,000,000 бутылок пива на стойке...

[править] Смотрите также

умный указатель с семантикой владения разделяемым объектом
(шаблон класса) [править]
(C++11)
слабая ссылка на объект, управляемый std::shared_ptr
(шаблон класса) [править]