есть связанные вопросы, такие как умные указатели + это считается вредным?, но они не обрабатывают мои кейс. Все эти вопросы касаются раскрытия необработанного указателя this в случае подсчета ссылок интеллектуальными указателями. Однако моя проблема не в том, что я выставляю this
, а использую его только в методе, но, возможно, в течение более длительного периода времени.
Рассмотрим следующий код:
class X{
void foo(){...} //Performs some long running task
}
shared_ptr<X> x;
void bar(){
x->foo();
}
Итак, некоторый код вызывает метод foo
объекта x. Учтите, что единственная разумная ссылка на экземпляр за x - это shared_ptr x. Теперь foo выполняет некоторую длительную задачу, вызывая другие функции и прочее.
Теперь представьте, что во время выполнения foo другой поток, обработчик сигнала или даже рекурсивный вызов в foo
изменяет или очищает ссылку x
. Это вызовет удаление объекта. Теперь указатель this
в стеке вызовов foo
указывает на удаленный объект. Поэтому дальнейшее выполнение кода в foo
приведет к непредсказуемым результатам.
Как этого избежать? Я имею в виду, что всякий раз, когда выполняется метод объекта, который обрабатывается с помощью подсчета ссылок, существует опасность того, что какой-то код может очистить ссылки на объект, и вызов метода завершится ошибкой или приведет к странным результатам. Проблема заключается в семантике: подсчет ссылок считает, что ссылки на объект больше нет, и поэтому удаляет его, но это неверно - ссылка все еще существует, но, к сожалению, это не умный указатель.
Я знаю, что есть такие вещи, как enable_shared_from_this
, но должен ли я ВСЕГДА переписывать все методы объектов, которые потенциально могут быть подсчитаны по ссылкам, чтобы сначала получить общий указатель от this
, а затем использовать этот указатель, чтобы избежать этой проблемы? Это загромождает весь код метода и, кроме того, все равно может выйти из строя: если событие очищается x
после того, как был сделан вызов foo, но до того, как будет выполнен первый оператор в методе (который получает общий экземпляр) (возможно, другой поток) , то снова произойдет сбой.
Итак, общий вопрос: как я могу быть в безопасности при использовании любого типа интеллектуальных указателей во время вызова метода к управляемому объекту? Как я могу гарантировать, что указатель this
внутри метода не станет недействительным? Является ли переписывание всех методов для использования enable_shared_from_this
в их первом операторе хорошим решением?
Или код, который очищает ссылку, пока эта ссылка все еще находится в стеке вызовов, просто неправильно сформирован? Но если это так, тогда трудно написать не плохо сформированный код, когда используются несколько потоков и сложные рекурсивные вызовы ...