Разберите, почему создание std::shared_ptr из this у уже управляемого объекта опасно.
Создание std::shared_ptr из this у объекта, которым уже владеет другой std::shared_ptr, формирует второй независимый блок управления. Оба блока считают себя единственными владельцами объекта, поэтому при обнулении последних ссылок объект может быть уничтожен дважды, что приводит к неопределённому поведению.
Для получения нового владельца уже управляемого объекта применяют std::enable_shared_from_this и его метод shared_from_this().
До появления интеллектуальных указателей управление динамической памятью обычно выполнялось вручную: один участок кода создавал объект, другой должен был правильно определить момент его уничтожения. При передаче указателя между компонентами было трудно установить, кто отвечает за освобождение ресурса.
std::shared_ptr решает задачу совместного владения с помощью блока управления, где хранятся счётчики владельцев и информация об уничтожении объекта. Важное условие этого подхода: все владельцы одного объекта должны быть связаны с одним блоком управления.
Выражение std::shared_ptr<T>(this) не проверяет, существует ли уже другой shared_ptr, управляющий тем же объектом. Оно безусловно создаёт новый блок управления и записывает в него тот же адрес.
В результате первый и второй shared_ptr не знают друг о друге. Когда каждый из них станет последним владельцем в своём блоке, каждый попытается вызвать уничтожение одного и того же объекта. Поведение программы становится неопределённым, а пользовательский удалитель может быть вызван повторно.
Отдельно опасна ситуация с объектом, который создан не через new, например с локальным или встроенным объектом. Тогда shared_ptr(this) может попытаться применить delete к памяти, которой нельзя управлять таким способом.
std::enable_shared_from_this<T> содержит служебное слабое представление связи с блоком управления. Когда объект впервые помещается в std::shared_ptr, эта связь настраивается, и последующий вызов shared_from_this() создаёт новый shared_ptr, увеличивающий счётчик именно исходного блока управления.
В примере first и second совместно владеют одним объектом и используют один блок управления. Объект уничтожится только после уничтожения последнего из них.
У shared_from_this() есть ограничение: он корректен только после того, как объект действительно был связан с shared_ptr. Вызов из конструктора или для объекта, созданного на стеке, не устанавливает такую связь; обычно это приводит к исключению std::bad_weak_ptr.
Если объект не должен поддерживать совместное владение, не следует создавать из него shared_ptr через this. Для временного обращения используют сырой указатель или ссылку при гарантированном времени жизни, а для хранения безопасной необязательной связи — std::weak_ptr.
shared_ptr из this иногда допустим в специально спроектированном типе, который гарантирует отсутствие другого блока управления и самостоятельно контролирует время жизни, но такой дизайн сложен и легко нарушается. В обычном коде безопаснее создавать объект через make_shared и применять enable_shared_from_this.
Сетевой объект должен передать себя в отложенный обработчик. Рассматривались три варианта: захватить this, создать shared_ptr(this) или получить владельца через shared_from_this().
Захват this не создаёт нового владельца: обработчик может выполниться после уничтожения объекта. shared_ptr(this) сохраняет объект дольше, но создаёт независимый блок управления и может привести к двойному уничтожению. Вариант с shared_from_this() корректно продлевает жизнь объекта, если обработчик действительно должен владеть им.
Если обработчик не должен продлевать жизнь объекта, лучше сохранить weak_ptr и перед выполнением попытаться получить временный shared_ptr через lock(). Такой вариант не удерживает объект живым без необходимости, но обработчик должен уметь корректно обработать ситуацию, когда объект уже уничтожен.
shared_from_this() в конструкторе объекта?Нет. В момент выполнения конструктора объект ещё не обязательно связан с управляющим shared_ptr, даже если вскоре будет создан через std::make_shared. Поэтому shared_from_this() в конструкторе обычно приводит к std::bad_weak_ptr.
Кроме того, публикация this из конструктора опасна сама по себе: внешний код может получить доступ к ещё не полностью сконструированному объекту. Безопаснее выполнять такую логику после завершения создания объекта.
shared_ptr через make_shared предпочтительнее обычного new?make_shared обычно размещает объект и служебные данные shared_ptr одной связной операцией выделения памяти. Это уменьшает число выделений и упрощает исключительную безопасность при создании объекта.
Однако совместное размещение означает, что память блока управления может сохраняться, пока живут weak_ptr, даже если сам объект уже уничтожен. При больших объектах или особых требованиях к пользовательскому удалителю вариант с отдельным new и собственным deleter может быть уместнее.
enable_shared_from_this несколько раз или с неверным типом?Связь с блоком управления должна быть однозначной и доступной для корректной настройки. Неоднозначное или неподходящее наследование может помешать библиотеке установить внутреннюю связь, поэтому shared_from_this() не будет работать как ожидается.
На практике тип обычно публично и однократно наследуют от std::enable_shared_from_this<СамТип>. Само наследование не создаёт владельца: объект всё равно должен быть первоначально передан в shared_ptr, после чего можно получать связанные копии через shared_from_this().