К какому моменту указатель raw становится недействительным после вызова drop? Рассмотрите код и назовите последствие обращения к нему.
В этом коде raw становится недействительным внутри drop, когда p.reset() уничтожает объект int: p был его единственным владельцем. Разыменование raw после возврата из drop — неопределённое поведение, потому что это висячий указатель.
RAII и умные указатели появились как средство связать управление ресурсом со временем жизни объекта-владельца. В стандартной библиотеке C++ std::shared_ptr предназначен для случаев, когда несколько объектов должны совместно владеть одним ресурсом.
При этом совместное владение не означает неизменность каждого указателя-владельца. Передача shared_ptr по неконстантной ссылке предоставляет функции право изменить сам объект-держатель: заменить управляемый ресурс, сбросить его или присвоить другой указатель.
До вызова drop переменная owner владеет объектом, а raw лишь указывает на него и не продлевает его время жизни. После reset() владелец освобождает объект, поэтому raw сохраняет только адрес уже уничтоженного объекта.
Попытка разыменовать такой указатель может вывести случайное значение, привести к сбою или внешне сработать без ошибки. Ни один из этих результатов не является допустимым поведением программы.
std::shared_ptr хранит ссылку на управляющий блок, где, в частности, находятся счётчик владельцев и информация об освобождении ресурса. Вызов reset() у owner уменьшает число владельцев; если оно становится нулём, управляемый объект уничтожается.
raw не участвует в подсчёте владельцев. Методы get() и неявное получение сырого указателя дают только временный доступ к адресу, но не передают владение и не создают дополнительной защиты времени жизни.
Передача по std::shared_ptr<int>& отличается от передачи по значению или по const std::shared_ptr<int>&. Неконстантная ссылка позволяет функции изменить исходный shared_ptr, поэтому такой параметр должен быть осознанным сигналом: функция может сбросить или заменить владение.
Если функции нужен только доступ к объекту, обычно используют const std::shared_ptr<int>&, ссылку на сам объект (int& или const int&) либо сырой указатель как явно невладеющий параметр. Если функция должна получить отдельное владение, применяют передачу shared_ptr по значению; если владение должно перейти однозначно — чаще подходит unique_ptr.
Безопасный вариант — не сохранять сырой указатель после операции, способной уничтожить объект:
Важно отличать уничтожение объекта от освобождения управляющего блока. Если существует другой shared_ptr, reset() у owner уничтожит не объект, а только владение owner; в исходном примере второго владельца нет.
В обработчике кэша функция получила std::shared_ptr<Resource>& и при ошибке вызвала reset(). Другой код ранее сохранил Resource* для быстрого доступа. После сброса кэша этот сырой указатель стал висячим, хотя проблема проявлялась лишь при редком сценарии ошибки.
Первый вариант — оставить сырой указатель. Он экономичен, но требует строгего протокола времени жизни и легко становится источником неопределённого поведения. Второй вариант — хранить копию shared_ptr в потребителе; это продлевает жизнь ресурса, но может задержать освобождение и скрыть границы владения.
Выбранное решение — передавать потребителю std::shared_ptr<const Resource> на время операции, а для кратковременного невладеющего доступа использовать ссылку с гарантией, что сброс кэша в этот период невозможен. Это явно разделяет владение и доступ, предотвращает висячий указатель и не создаёт долгоживущих владельцев без необходимости.
Вопрос: Безопасно ли передать shared_ptr по значению вместо неконстантной ссылки, если функция всё равно вызывает reset() у своего параметра?
Ответ: Да, с точки зрения исходного владельца это безопаснее. Функция сбросит только свою копию, а исходный shared_ptr останется владельцем; объект не уничтожится, пока сохраняется исходное владение. Цена — операция копирования и увеличение счётчика владельцев, хотя перемещение может избежать копирования, если интерфейс допускает передачу владения.
Вопрос: Продлевает ли время жизни объекта копирование сырого указателя, полученного через get()?
Ответ: Нет. Копируется только адрес, а не запись в управляющем блоке. Время жизни продлевает копирование самого std::shared_ptr, а не T*; поэтому сырой указатель нужно рассматривать как невладеющий.
Вопрос: Что изменится, если перед вызовом drop(owner) создать auto backup = owner?
Ответ: У объекта появятся два владельца. После drop(owner) счётчик владельцев уменьшится, но backup продолжит владеть объектом, поэтому raw ещё будет указывать на живой объект. Однако полагаться на такую косвенную гарантию не следует: если backup уничтожить или сбросить до использования raw, указатель снова станет недействительным.