RAII и стек раскручивается
До сих пор мои представления о "переплетении" (из-за отсутствия лучшего слова) RAII и раскручивания стека были / были (если не полностью) неправильными. Насколько я понимаю, использование RAII защищает от любых / всех утечек ресурсов, даже тех, которые могут быть вызваны необработанными исключениями.
Однако написание этой тестовой программы и впоследствии наткнувшись на эту статью / документацию, заставило меня осознать, что раскручивание стека вызовет только то, что освобождение ресурса с поддержкой RAII сработает только для автоматических в блоке try, а не для автоматических в, скажем, внешних / других областях.,
Я прав в этом (новом) понимании? Или есть еще нюансы, которые я еще не уловил? Любой гуру хочет присоединиться? Указатели на любые хорошие рецензии / анализы / объяснения (раскручивания стека) были бы полезны / оценены…
3 ответа
Из стандарта C++03, §15.3/9:
Если в программе не найден соответствующий обработчик, вызывается функция terminate (); действительно ли стек разматывается до того, как этот вызов terminate () определяется реализацией (15.5.1).
§15.5.1 / 1:
В следующих ситуациях обработка исключений должна быть прекращена для менее тонких методов обработки ошибок: ... когда механизм обработки исключений не может найти обработчик для брошенного исключения (15.3)...
§15.5.1 / 2:
В таких случаях,
void terminate ();
называется (18.6.3). В ситуации, когда соответствующий обработчик не найден, определяется реализацией, будет ли стек разматываться перед вызовом terminate (). Во всех других ситуациях стек не должен быть размотан до вызова terminate (). Реализация не имеет права досрочно завершать разматывание стека на основании определения, что процесс размотки в конечном итоге вызовет вызов terminate ().
Вы правы, что "раскручивание стека" происходит на пути от throw some_exception
в catch(some_exception)
, Если ваше исключение никогда не достигнет цели, мы не знаем, что произойдет.
Это большая проблема? Как вы показали себя, вам просто нужно добавить catch(...)
где-то, чтобы поймать все возможные исключения, и проблема уходит.
Стандарт определяет три способа прекращения выполнения программы на C++:
- Вернуться из
main
, Объекты с автоматическим хранением (function-local) уже уничтожены. Объекты со статическим хранилищем (глобальные, статические, функциональные) будут уничтожены. std::exit
от<cstdlib>
, Объекты с автоматическим хранением НЕ уничтожаются. Объекты со статическим хранилищем будут уничтожены.std::abort
от<cstdlib>
, Объекты с автоматическим и статическим хранением НЕ уничтожаются.
Также актуальным является std::terminate
от <exception>
, Поведение terminate
можно заменить с помощью std::set_terminate
, но terminate
всегда должен "прекратить выполнение программы", вызвав abort
или некоторая аналогичная, зависящая от реализации альтернатива. По умолчанию просто { std::abort(); }
,
С ++ будет звонить std::terminate
всякий раз, когда выдается исключение, и C++ не может разумно выполнить раскрутку стека. Например, исключение из деструктора, вызываемого разматыванием стека, или исключение из статического конструктора или деструктора объекта хранения. В этих случаях (больше) разматывание стека не выполняется.
C++ также вызовет std::terminate
когда совпадение catch
обработчик не найден. В этом единственном случае C++ может по желанию main
перед звонком terminate
, Так что ваш пример может иметь разные результаты с другим компилятором.
Так что, если вы правильно используете RAII, оставшиеся шаги для "утечки" вашей программы:
- избежать
std::abort
, - Либо избегайте
std::exit
или избегайте всех объектов со статической продолжительностью хранения. - Положить
catch (...)
обработчик вmain
и убедитесь, что в нем или после него не происходит никаких распределений или исключений. - Избегайте других ошибок программирования, которые могут вызвать
std::terminate
,- (В некоторых реализациях функции, скомпилированные с помощью компилятора C, ведут себя так, как будто они имеют C++ пустой
throw()
спецификация, означающая, что исключения не могут быть "пропущены" за ними, даже если у них нет деструкторов, которые можно вызвать.)
- (В некоторых реализациях функции, скомпилированные с помощью компилятора C, ведут себя так, как будто они имеют C++ пустой