Namespaces
Variants

std:: unique_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (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)
(C++11) (until C++23)
Определено в заголовочном файле <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 . Если время жизни объекта управляется const std :: 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; или другого fancy 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-ссылка на функцию или функциональный объект, вызываемый из деструктора

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

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

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

создает уникальный указатель, управляющий новым объектом
(шаблон функции)
сравнивает с другим 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, может принимать его по значению или по rvalue-ссылке
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()
    {
        // уничтожить узлы списка последовательно в цикле, стандартный деструктор
        // вызвал бы деструктор своего "next" рекурсивно, что бы
        // вызывает переполнение стека для достаточно больших списков.
        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';
    } // “close_file()” вызывается здесь (если “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++.

DR Applied to Behavior as published Correct behavior
LWG 4144 C++11 T* was not required to form a valid type required

Смотрите также

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