перервати, припинити чи вийти?


112

Яка різниця між цими трьома, і як я можу закінчити програму у випадку виключення, з яким я не можу справитись належним чином?


3
Це не дублікат, а, скоріше, підмножина з хорошими відповідями stackoverflow.com/questions/397075/…, і вона була позначена також C ++!
Еллі Кессельман

std::abortє розумним, якщо виняток не можна вирішити в деструкторі.
Даніель

1
Для отримання додаткової інформації про std::terminateці статті в чудовому блозі C ++ Анджея: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Шнайдер

Відповіді:


3

Моя порада буде не користуватися жодною з них. Натомість catchвинятки, з якими ви не можете впоратися main()і просто returnзвідти. Це означає, що вам гарантовано, що розмотування стека відбувається правильно і викликаються всі деструктори. Іншими словами:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}

8
@Neil: Погоджено в основному, але винятки, які програма не може впоратися, слід повідомити та повторно скинути. Нехай програма втратить роботу.
Джон Дайлінг

13
Щоб переконатися, що стек розмотається, ви завжди повинні брати участь у головному. Але я б перекинувся з улову. Оскільки деякі ОС мають можливість автоматично викликати налагоджувальну інфраструктуру, якщо ви компілювали в налагодження.
Мартін Йорк

5
Винятки, на які не потрапляє навіть обробник верхнього рівня, можуть викликати систему системного звітування, яка скидає процес і завантажує звіт про винятки для уваги розробників, як-от Windows Report Error Reporting, звіти про помилки Mac OS X та журнали помилок додатків iPhone.
JBRWilkinson

6
@John Причина, чому мене це дивує, полягає в тому, що, хоча це має ідеальний сенс у світлі того, як реально застосовуються винятки, він порушує абстракцію, що виняток "поширює стек", поки не буде знайдено відповідний обробник (або припинити називається). І непрохідні абстракції, хоча вони часто неминучі, обов'язково дивують, коли стикаються.
Тайлер Мак-Генрі

11
-1 тому, що це не відповідає половині запитання. "Яка різниця між [скасувати, припинити чи вийти?]" Це краща відповідь: stackoverflow.com/a/397081/353094 Також stackoverflow.com/a/2820407/353094 - чудова відповідь.
leetNightshade

149
  • abort вказує на "ненормальний" кінець програми та піднімає POSIX-сигнал SIGABRT, а це означає, що будь-який обробник, який ви зареєстрували для цього сигналу, буде викликаний, хоча програма все одно припиняє післямови в будь-якому випадку. Зазвичай ви використовуєте abortв програмі C для виходу з несподіваного випадку помилки, коли помилка, ймовірно, є помилкою в програмі, а не щось на зразок поганого вводу чи відмови мережі. Наприклад, ви можете, abortякщо в структурі даних було виявлено вказівник NULL, коли це логічно не повинно відбуватися.

  • exit вказує на "нормальний" кінець програми, хоча це все ще може означати збій (але не про помилку). Іншими словами, ви можете мати exitкод помилки, якщо користувач дав введення, яке неможливо було розібрати, або файл не вдалося прочитати. Вихідний код 0 вказує на успіх. exitтакож необов'язково викликає обробників до завершення програми. Вони зареєстровані за допомогою atexitта on_exitфункцій.

  • std :: термінація - це те, що автоматично викликається в програмі C ++, коли є необроблений виняток. Це, по суті, еквівалент C ++ abort, якщо припустити, що ви повідомляєте про всі свої виняткові помилки за допомогою викидання виключень. Це викликає обробник, встановлений std::set_terminateфункцією, який за замовчуванням просто викликає abort.

У C ++, як правило, ви хочете уникати дзвінків abortабо exitпомилок, оскільки вам краще кидати винятки і надавати код додатково вгору стеку викликів, щоб вирішити, чи закінчується програма чи ні. Незалежно від того, використовуєте ви чи не exitдля успіху, це - обставина - чи має сенс закінчувати програму де-небудь, крім заяви про повернення в main.

std::terminateслід вважати інструментом звітності про помилки останнього канаву навіть у C ++. Проблема зstd::terminate полягає в тому, що обробник терміналу не має доступу до винятку, який пройшов без обробки, тому немає можливості сказати, що це було. Зазвичай вам набагато краще загортати все основне в try { } catch (std::exception& ex) { }блок. Принаймні, тоді ви можете повідомити більше інформації про винятки, які випливають із цього std::exceptionвиду (хоча, звичайно, винятки, які не випливають із std::exceptionцього, все-таки закінчуються без змін).

Згорнути тіло mainв try { } catch(...) { }не набагато краще, ніж встановити обробник терміналу, тому що знову не маєте доступу до відповідного винятку. Редагувати: Відповідь Ніла Баттерворта є користю в тому, що стек розкручується в цьому випадку, що (дещо дивно) не відповідає дійсності без обліку.


10
Чи можете ви оновити цю відповідь інформацією C ++ 11? Здається, зараз існують способи отримати виняток у catch (...) та в оброблювачі завершення.
Клайм

1
У C ++ обробник припиняє робить доступ до виключення через std::current_exception(). Дивіться приклад тут: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm

Не має значення, що ви можете отримати поточний виняток, оскільки не можете його перевірити. Все, що ви можете зробити, це повторно кинути його.
seattlecpp

2
@seattlecpp ви можете повторно скинути його і зафіксувати посилання на нього, яке ви зможете оглянути
gpeche

16

std :: abort і std :: exit (і більше: std :: _ Exit, std :: quick_exit) - це лише функції нижчого рівня. Ви використовуєте їх, щоб сказати програмі, що ви хочете, щоб вона точно робила: які деструктори (і якщо) викликати, які інші функції очищення потрібно викликати, яке значення повернути тощо.

std :: термінація - це абстракція вищого рівня: її викликають (або час виконання, або ви), щоб вказати, що сталася помилка в програмі і чомусь неможливо впоратися, кинувши виняток. Необхідність цього зазвичай виникає, коли в самому механізмі винятку виникає помилка, але ви можете використовувати її будь-коли, коли не хочете, щоб ваша програма продовжувалась перевищувати задану помилку. Я склав повний перелік ситуацій, коли викликається std :: endinate в моєму посту. Не вказано, що робить std :: термінал, оскільки ви цим керуєте. Ви можете налаштувати поведінку, зареєструвавши будь-які функції. У вас є обмеження, що функція не може повернутися на сайт помилок і не може вийти за винятком, але технічно ви навіть можете запустити насос повідомлення всередині. Перелік корисних речей, які ви можете зробити всередині, дивіться в іншому моєму дописі .

Зокрема, зауважте, що std :: термінат вважається обробником винятків у контекстах, де std :: термінал викликається через викинуте виняток, який не можна було обробити, і ви можете перевірити, що було винятком, і перевірити його за допомогою C ++ 11, використовуючи std :: rethrow_exception та std :: current_exception. Це все в моєму пості .


Чи доцільно мати обробник очищення, якщо програма припиняється через сигнали системи? Наприклад, недійсний доступ до пам'яті призводить до генерування сигналу SIGSEGV. У цьому випадку, чи добре просто дозволити програмі завершити роботу та мати основний файл або зареєструвати обробник сигналу для очищення ?? Чи є проблеми з очищенням під час обробки системних сигналів порівняно з очищенням під час обробки std :: термінації?
kartik trivikram

12

quick_exit () !

Якщо ваша програма є багатопотоковою, то виклик exit(), швидше за все, призведе до збоїв, оскільки глобальний / статичнийstd::thread об'єкти будуть намагатися знищити, не виходячи з їх потоків.

Якщо ви хочете повернути код помилки та вийти з програми (більш-менш) звичайно, зателефонуйте quick_exit()у багатопотокові програми. Для аномального припинення (без можливості вказати код помилки) abort()або std::terminate()можна викликати.

Примітка: quick_exit () не підтримувався MSVC ++ до версії 2015 року.


4
  • термін припинення залишає вам можливість зареєструвати, що буде, коли воно буде викликано. Повинна бути одна з двох інших.
  • exit - це нормальний вихід, що дозволяє визначити стан виходу. Запущені обробники, зареєстровані at_exit ()
  • аборт - ненормальний вихід. Єдине, що виконується - це обробник сигналу для SIGABRT.

4
  • термін терміналу () автоматично викликається, коли трапляється виняток, який неможливо обробити. За замовчуванням припиніть () виклики перервати (). Ви можете встановити власну ручку за допомогою функції set_terminate ().

    abort () посилає сигнал SIGABRT.

    exit () - це не обов'язково погано. Він успішно виходить із програми та викликає функції atexit () у порядку LIFO. Я зазвичай не бачу цього в додатках C ++, однак я бачу це у багатьох програмах на базі Unix, де в кінці він надсилає вихідний код. Зазвичай вихід (0) вказує на успішний запуск програми.


8
Невдало! І в Unix, і в DOS, вихід (0) вказує на успіх, а будь-яке інше значення, передане для виходу (), вказує на збій, а не навпаки!
Річард Баррелл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.