Вопрос 50: Умные указатели: уникальный указатель
Умный указатель (англ. smart pointer) — класс (обычно шаблонный), имитирующий интерфейс обычного указателя и добавляющий некую новую функциональность, например проверку границ при доступе или очистку памяти.
Чаще всего умный указатель инкапсулирует семантику владения ресурсом. В таком случае он называется владеющим указателем.
Владеющие указатели применяются для борьбы с утечками памяти и висячими ссылками. Утечкой памяти называется ситуация, когда в программе нет ни одного указателя, хранящего адрес объекта, созданного в динамической памяти. Висячей ссылкой называется указатель, ссылающийся на уже удалённый объект. Семантика владения для динамически созданных объектов означает, что удаление или присвоение нового значения указателю будет согласовано с временем жизни объекта.
В стандарте C++11 появились следующие умные указатели: unique_ptr, shared_ptr и weak_ptr. Все они объявлены в заголовочном файле <memory>
.
Уникальный указатель (unique_ptr)
Этот указатель пришел на смену старому и проблематичному auto_ptr. Основная проблема последнего заключается в правах владения. Объект этого класса теряет права владения ресурсом при копировании (присваивании, использовании в конструкторе копий, передаче в функцию по значению).
std::auto_ptr<int> x_ptr(new int(42));
std::auto_ptr<int> y_ptr;
// вот это нехороший и неявный момент
// права владения ресурсов уходят в y_ptr и x_ptr начинает
// указывать на null pointer
y_ptr = x_ptr;
// segmentation fault
std::cout << *x_ptr << std::endl;
В отличии от auto_ptr, unique_ptr запрещает копирование.
std::unique_ptr<int> x_ptr(new int(42));
std::unique_ptr<int> y_ptr;
// ошибка при компиляции
y_ptr = x_ptr;
// ошибка при компиляции
std::unique_ptr<int> z_ptr(x_ptr);
Изменение прав владения ресурсом осуществляется с помощью вспомогательной функции std::move (которая является частью механизма перемещения).
std::unique_ptr<int> x_ptr(new int(42));
std::unique_ptr<int> y_ptr;
// также, как и в случае с ``auto_ptr``, права владения переходят
// к y_ptr, а x_ptr начинает указывать на null pointer
y_ptr = std::move(x_ptr);
Как auto_ptr, так и unique_ptr обладают методами reset(), который сбрасывает права владения, и get(), который возвращает сырой (классический) указатель.
std::unique_ptr<Foo> ptr = std::unique_ptr<Foo>(new Foo);
// получаем классический указатель
Foo *foo = ptr.get();
foo->bar();
// сбрасываем права владения
ptr.reset();
Как видно, unique_ptr недалеко ушел от своего предшественника в плане удобства использования, но, во всяком случае, он обезопасил от неявных смен прав владений ресурсом.