std:: unique_ptr
|
Определено в заголовочном файле
<memory>
|
||
|
template
<
class
T,
|
(1) | (начиная с C++11) |
|
template
<
class
T,
|
(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
:
- Управляет одним объектом (например, выделенным с помощью new ).
- Управляет динамически выделенным массивом объектов (например, выделенным с помощью 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) |
|
Версия для одиночного объекта,
|
|
|
разыменовывает указатель на управляемый объект
(public member function) |
|
Версия для массива,
|
|
|
предоставляет индексированный доступ к управляемому массиву
(public member function) |
|
Функции, не являющиеся членами класса
|
(C++14)
(C++20)
|
создает уникальный указатель, управляющий новым объектом
(шаблон функции) |
|
(удален в C++20)
(C++20)
|
сравнивает с другим
unique_ptr
или с
nullptr
(шаблон функции) |
|
(C++20)
|
выводит значение управляемого указателя в выходной поток
(шаблон функции) |
|
(C++11)
|
специализирует алгоритм
std::swap
(шаблон функции) |
Вспомогательные классы
|
(C++11)
|
Поддержка хеширования для
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
типа
(класс) |