RAII и стек раскручивается - PullRequest
0 голосов
/ 05 апреля 2011

TIL, что мои представления о «переплетении» (из-за отсутствия лучшего слова) RAII и раскручивания стека были / были (если не полностью) неправильными. Насколько я понимаю, использование RAII защищает от любых / всех утечек ресурсов, даже тех, которые могут быть вызваны необработанными исключениями.

Однако написание этой тестовой программы и впоследствии наткнувшись на эту статью / документацию , заставило меня осознать, что раскрутка стека вызовет только то, что освобождение ресурса с поддержкой RAII сработает для автоматических внутри блок try, в отличие от автоматического, скажем, во внешних / других областях.

Прав ли я в этом (новом) понимании? Или есть еще нюансы, которые я еще не уловил? Любой гуру хочет присоединиться? Указатели на любые хорошие рецензии / анализы / объяснения (раскручивания стека) были бы полезны / оценены…

Ответы [ 3 ]

5 голосов
/ 05 апреля 2011

Из стандарта 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 ().

2 голосов
/ 06 апреля 2011

Вы правы, что "раскрутка стека" происходит на пути от throw some_exception до catch(some_exception). Если ваше исключение никогда не достигнет цели, мы не знаем, что произойдет.

Это большая проблема? Как вы показали, вам просто нужно добавить куда-нибудь catch(...), чтобы перехватить все возможные исключения, и проблема исчезнет.

0 голосов
/ 06 апреля 2011

Стандарт определяет три способа завершения выполнения программы на C ++:

  • Возврат из main. Объекты с автоматическим хранением (function-local) уже уничтожены. Объекты со статическим хранилищем (глобальные, статические, функциональные) будут уничтожены.
  • std::exit от <cstdlib>. Объекты с автоматическим хранением НЕ уничтожены. Объекты со статическим хранилищем будут уничтожены.
  • std::abort из <cstdlib>. Объекты с автоматическим и статическим хранением НЕ уничтожены.

Также имеет значение std::terminate из <exception>. Поведение terminate можно заменить с помощью std::set_terminate, но terminate всегда должно "завершать выполнение программы", вызывая abort или какую-либо аналогичную для реализации альтернативу. По умолчанию это просто { std::abort(); }.

C ++ будет вызывать std::terminate всякий раз, когда выдается исключение, и C ++ не может разумно выполнить разматывание стека. Например, исключение из деструктора, вызываемого разматыванием стека, или исключение из статического конструктора или деструктора объекта хранения. В этих случаях (больше) разматывание стека не выполняется.

C ++ также вызовет std::terminate, если соответствующий обработчик catch не найден. В этом единственном случае C ++ может опционально раскрутиться до main перед вызовом terminate. Так что ваш пример может иметь разные результаты с другим компилятором.

Так что, если вы правильно используете RAII, оставшиеся шаги для «утечки» вашей программы:

  • Избегайте std::abort.
  • Либо избегайте std::exit, либо избегайте всех объектов со статической продолжительностью хранения.
  • Поместите обработчик catch (...) в main и убедитесь, что в нем или после него не происходит выделений или исключений.
  • Избегайте других ошибок программирования, которые могут вызвать std::terminate.
    • (В некоторых реализациях функции, скомпилированные с помощью компилятора C, действуют так, как будто у них есть пустая спецификация throw() в C ++, что означает, что исключения не могут быть "пропущены" через них, даже если у них нет деструкторов для вызова.)
...