Для чего нужен weak_ptr в C++ и какую задачу без него не решить
Что проверяют
Понимаете ли вы модель владения в shared_ptr и можете ли назвать задачу, которую сырой указатель в принципе не решает. Следом часто просят объяснить, почему lock() правильнее связки expired() и разыменования и что происходит с памятью, пока жив хотя бы один weak_ptr.
Как отвечать
std::weak_ptr это невладеющая ссылка на объект под управлением shared_ptr. Он не увеличивает счётчик сильных ссылок, поэтому не продлевает объекту жизнь, но умеет узнать, жив ли тот:
lock(): атомарно возвращаетshared_ptr, пустой, если объект уже уничтожен. Пока полученныйshared_ptrжив, объект не удалят из другого потока;expired(): годится только как подсказка. Между проверкой и использованием объект может исчезнуть, поэтому рабочий шаблон один:if (auto p = w.lock()) { ... };- блок управления: хранит два счётчика. Объект разрушается, когда обнулились сильные ссылки, а сам блок живёт, пока есть слабые.
Задача, которую без него не решить: узнать, жив ли объект, которым вы не владеете. Сырой указатель после удаления объекта становится висячим, и проверить это нельзя. Отсюда типовые применения:
- разрыв циклов: родитель владеет детьми через
shared_ptr, ребёнок ссылается на родителя черезweak_ptr. Дваshared_ptrдруг на друга дают утечку, счётчики никогда не дойдут до нуля; - колбэки и наблюдатели: в асинхронный обработчик захватывают
weak_from_this()изenable_shared_from_this(C++17), и если объект успел умереть, колбэк просто ничего не делает; - кэши: словарь из
weak_ptrотдаёт объект, пока им кто-то пользуется, и не держит его сверх этого.
Где спотыкаются
Говорят, что weak_ptr не даёт объекту удалиться. Ровно наоборот: он наблюдает, а не владеет, и объект удаляется вместе с последним shared_ptr.
Проверяют expired(), а потом берут lock() и не проверяют результат. В многопоточном коде это гонка.
Не знают про побочный эффект make_shared: объект и блок управления лежат в одном выделении памяти. Деструктор отработает вовремя, но сама память освободится только после смерти последнего weak_ptr, и для больших объектов с долгоживущими слабыми ссылками это заметно.