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). По умолчанию средство удаления использует оператор delete, который уничтожает объект и освобождает память.
Альтернативно unique_ptr
может не владеть никаким объектом, и в этом случае он называется пустым.
Существует две версии std::unique_ptr
:
- Управляет отдельным объектом (например, распределённым с помощью new)
- Управляет динамически распределяемым массивом объектов (например, распределённым с помощью 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 функция-элемент) | |
Однообъектная версия,
| |
разыменовывает указатель на управляемый объект (public функция-элемент) | |
Версия массива,
| |
обеспечивает индексированный доступ к управляемому массиву (public функция-элемент) |
[править] Функции, не являющиеся элементами
(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, может принимать его по значению или по правосторонней // ссылке 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) |
умный указатель с семантикой владения разделяемым объектом (шаблон класса) |
(C++11) |
слабая ссылка на объект, управляемый std::shared_ptr (шаблон класса) |