В классе, которым управляет std::shared_ptr, метод должен вернуть std::shared_ptr на тот же объект без создания второго владельца. Как устроить это безопасно с помощью std::enable_shared_from_this?
Унаследуйте класс от std::enable_shared_from_this<T> и внутри метода вызывайте shared_from_this(). Этот вызов возвращает новый объект std::shared_ptr, который разделяет уже существующий control block, поэтому не создаёт второго независимого владельца и не приводит к двойному уничтожению.
Объект должен быть предварительно создан под управлением std::shared_ptr, обычно через std::make_shared. Вызов shared_from_this() до этого момента завершается исключением std::bad_weak_ptr.
Проблема возникает из-за различия между указателем this и владением объектом. this указывает на текущий объект, но сам по себе не содержит информации о существующем control block std::shared_ptr.
Попытка построить std::shared_ptr из this создаёт новый control block. Если объект уже принадлежит другому std::shared_ptr, появляются два независимых владельца, каждый из которых считает себя ответственным за уничтожение объекта. std::enable_shared_from_this предоставляет стандартный способ получить дополнительный std::shared_ptr, связанный с исходным владением.
Метод объекта может передать самого себя в асинхронную операцию, очередь задач или callback. Простое создание std::shared_ptr из this в такой ситуации опасно: исходный и новый указатели не знают друг о друге, а значит, могут дважды вызвать деструктор.
Передача this без увеличения времени жизни тоже может быть ошибочной. Если внешний std::shared_ptr уничтожит объект до выполнения callback, callback будет хранить висячий указатель.
Класс публично и однозначно наследуют от std::enable_shared_from_this<T>. При создании первого std::shared_ptr для объекта библиотека связывает внутренний weak-интерфейс базового класса с существующим control block. Затем shared_from_this() временно создаёт std::shared_ptr из этой связи, увеличивая число владельцев того же control block.
В примере task и same указывают на один объект и используют один control block. Уничтожение одного из них только уменьшает счётчик владельцев; объект уничтожается после исчезновения последнего std::shared_ptr.
Важно, что shared_from_this() не продлевает жизнь объекта сам по себе, пока возвращённый std::shared_ptr не сохранён или не передан дальше. Если нужен необязательный доступ без продления времени жизни, применяют weak_from_this(), а затем пытаются получить временный владеющий указатель через lock().
Вызов shared_from_this() в конструкторе обычно некорректен: объект ещё не успел быть связан с control block внешним std::shared_ptr. То же ограничение действует для объекта, созданного на стеке или управляемого только std::unique_ptr.
Наследование должно быть доступным для механизма std::shared_ptr: приватное или неоднозначное наследование может помешать корректной инициализации связи. Кроме того, std::enable_shared_from_this не делает обычные операции над полями объекта потокобезопасными: он синхронизирует владение control block, но не произвольный доступ к состоянию Task.
Сервис запускает фоновую задачу, которая позже обращается к объекту-сотруднику. Вариант с захватом сырого this не удерживает объект в живых. Вариант с созданием нового std::shared_ptr из this удерживает его формально, но создаёт независимый control block и может привести к двойному удалению.
Вариант с захватом shared_from_this() безопасно продлевает жизнь объекта, но может создать цикл: объект хранит callback, а callback хранит shared_ptr на этот же объект. В таком случае объект не освободится.
Практичное решение — использовать shared_from_this(), когда выполнение задачи действительно должно гарантированно завершиться на живом объекте. Если задача не должна владеть объектом, callback хранит weak_ptr, полученный через weak_from_this(), и перед выполнением вызывает lock(). Это предотвращает и обращение к уничтоженному объекту, и ненужное продление его жизни.
Нет, надёжно рассчитывать на это нельзя. Сначала выполняется конструирование объекта, а затем std::shared_ptr завершает настройку связи std::enable_shared_from_this с control block. Поэтому в конструкторе shared_from_this() может выбросить std::bad_weak_ptr. Инициализацию, требующую shared_ptr на this, следует перенести в отдельный метод, вызываемый после создания объекта.
Будут созданы два независимых control block. Каждый указатель будет считать себя единственным владельцем и при достижении нулевого счётчика попытается уничтожить один и тот же объект. Это приводит к неопределённому поведению, обычно проявляющемуся как двойное освобождение. std::enable_shared_from_this нужен именно для получения указателя из уже существующего control block.
shared_from_this() добавляет владельца и тем самым гарантирует существование объекта до завершения callback, но может продлить его жизнь дольше ожидаемого или создать цикл через хранимый callback. weak_from_this() владение не увеличивает. В момент выполнения callback вызывается lock(): если объект ещё жив, получается временный std::shared_ptr; если объект уже уничтожен, возвращается пустой указатель, и callback корректно прекращает работу.