Ответ
Умные указатели (unique_ptr, shared_ptr, weak_ptr) решают многие проблемы с ручным управлением памятью, но имеют свои нюансы:
-
Циклические ссылки (только для
shared_ptr). Если два объекта владеют друг другом черезshared_ptr, счётчики ссылок никогда не обнулятся, что приводит к утечке памяти.struct Node { std::shared_ptr<Node> other; }; auto a = std::make_shared<Node>(); auto b = std::make_shared<Node>(); a->other = b; // a владеет b b->other = a; // b владеет a -> ЦИКЛ // a и b не будут уничтожены после выхода из области видимостиРешение: Разрывать циклы, заменяя одну из ссылок на
weak_ptr. -
Накладные расходы.
shared_ptrхранит два указателя (на объект и на блок управления с счётчиками) и выполняет атомарные операции для изменения счётчика ссылок, что дороже сырого указателя. -
Проблемы совместимости с массивами.
shared_ptr<T>по умолчанию используетdelete, а неdelete[]. Для массивов нужно указывать кастомный делитер.// Правильно для массива std::shared_ptr<int[]> arr(new int[10], std::default_delete<int[]>()); // Или, начиная с C++17: std::shared_ptr<int[]> arr(new int[10]); // unique_ptr поддерживает массивы "из коробки": std::unique_ptr<int[]> u_arr(new int[10]); -
Риск "прострела себе в ногу" при использовании
get(). Передача сырого указателя, полученного черезptr.get(), в код, который может сохранить его или удалить, нарушает семантику владения и ведёт к UB или двойному удалению. -
Неявные преобразования.
shared_ptrнеявно преобразуется из совместимых типов, что иногда может скрывать ошибки.unique_ptrболее строгий и требует явного перемещения (std::move).