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). По умолчанию средство удаления использует оператор 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 (шаблон класса) |