Вопрос 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 недалеко ушел от своего предшественника в плане удобства использования, но, во всяком случае, он обезопасил от неявных смен прав владений ресурсом.

results matching ""

    No results matching ""