ПрограммированиеC++ Middle разработчик

Объясните различия между обычным указателем и умным указателем (например, std::unique_ptr) в C++. Почему умные указатели делают программы надёжнее?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса:

Обычные (сырые) указатели — традиционный механизм в C++ для работы с динамической памятью. Это универсальный абстрактный механизм, но, к сожалению, очень подвержен ошибкам: утечкам памяти, двойному удалению, ошибкам Dangling pointer. Поэтому с C++11 стандартная библиотека включает умные указатели — шаблонные классы (std::unique_ptr, std::shared_ptr, std::weak_ptr), которые автоматически управляют временем жизни объекта.

Проблема:

При использовании обычных указателей ответственность за выделение и освобождение памяти ложится на программиста. Ошибки в освобождении памяти приводят к утечкам (memory leaks), разрушению данных, падениям программы. Особенно сложные случаи встречаются в обработке исключений или при передаче указателей в другие функции.

Решение:

Умные указатели инкапсулируют выделенную память и автоматически освобождают её, когда владельцев больше нет. Они реализуют RAII. std::unique_ptr обеспечивает эксклюзивное владение, std::shared_ptr — разделённое, std::weak_ptr — неконтролирующее (для предотвращения "цикла ссылок").

Пример кода:

#include <memory> void foo() { std::unique_ptr<int> p = std::make_unique<int>(5); // память освободится даже при исключении // ... }

Ключевые особенности:

  • Умные указатели освобождают ресурсы при уничтожении автоматически
  • std::unique_ptr запрещает копирование, только перенос семантики (move)
  • std::shared_ptr реализует "подсчёт ссылок"

Вопросы с подвохом.

Можно ли использовать std::unique_ptr в массив, созданный через new[]?

Нет, для массивов используйте std::unique_ptr<T[]>: так будет вызван delete[] вместо delete.

std::unique_ptr<int[]> arr(new int[10]);

Стандартные умные указатели могут предотвратить все возможные утечки памяти?

Нет. Например, циклические ссылки между std::shared_ptr приведут к утечкам. Для разрыва таких циклов применяют std::weak_ptr.

Могут ли умные указатели "удалить" один и тот же объект дважды?

Нет, если не нарушать логику владения (не использовать сырые указатели!). Если вручную копировать сырой указатель, то уничтожение может произойти дважды.

Типовые ошибки и анти-паттерны

  • Копировать std::unique_ptr — приведёт к ошибке компиляции
  • Использовать одновременно умный и сырой указатель на один объект
  • Не использовать std::weak_ptr для решения кольцевых зависимостей

Пример из жизни

Негативный кейс

Используются сырые указатели, память освобождается вручную, при выбросе исключения память не освобождается.

Плюсы:

  • Понятно в простых задачах

Минусы:

  • Регулярные утечки памяти
  • Потенциальное двойное удаление
  • Сложно сопровождать код

Позитивный кейс

Используется std::unique_ptr и std::make_unique для создания объектов и передачи их в функции.

Плюсы:

  • Практически невозможна утечка памяти
  • RAII-интерфейс, простое владение и передача объектов

Минусы:

  • Требует понимания move-семантики и работы стандартной библиотеки