прервать, прекратить или выйти?

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
Чтобы стек раскручивался, всегда нужно ловить main. Но я бы перебросил из улова. Поскольку некоторые ОС имеют возможность автоматически вызывать инфраструктуру отладки, если вы скомпилировали отладку.
Мартин Йорк,
5
Исключения, которые не обнаруживаются даже обработчиком верхнего уровня, могут вызвать средство системной отчетности, которое выгружает процесс и выгружает отчет об исключении для внимания разработчиков, например отчеты об ошибках Windows, отчеты об ошибках Mac OS X и журналы ошибок приложений iPhone.
JBRWilkinson
6
@John Причина, по которой меня удивляет, заключается в том, что, хотя это имеет смысл в свете того, как на самом деле реализуются исключения, он нарушает абстракцию, согласно которой исключение «распространяется вверх по стеку», пока не будет найден подходящий обработчик (или завершится не называется). А дырявые абстракции, часто неизбежные, неизбежно вызывают удивление, когда встречаются.
Тайлер МакГенри
11
-1, потому что это не отвечает на половину вопроса. «В чем разница между [abort, terminate или exit?]» Это лучший ответ: 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 :: terminate - это то, что автоматически вызывается в программе C ++ при возникновении необработанного исключения. По сути, это эквивалент C ++ abort, предполагающий, что вы сообщаете обо всех своих исключительных ошибках посредством выдачи исключений. Это вызывает обработчик, установленный std::set_terminateфункцией, которая по умолчанию просто вызывает abort.

В C ++ обычно нужно избегать вызова abortили exitошибки, поскольку лучше генерировать исключение и позволять коду дальше по стеку вызовов решать, подходит ли завершение программы. Независимо от того, используете ли вы exitдля успеха или нет, зависит от обстоятельств - имеет ли смысл завершить программу где-нибудь, кроме оператора return в main.

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

Обернуть тело mainin try { } catch(...) { }ненамного лучше, чем установить обработчик завершения, потому что снова у вас нет доступа к рассматриваемому исключению. Изменить: ответ Пер Нила Баттерворта, есть преимущество в том, что в этом случае стек разматывается, что (несколько удивительно) неверно для необработанного исключения.

Тайлер МакГенри
источник
10
Можете ли вы обновить этот ответ информацией о C ++ 11? Кажется, что теперь есть способы получить исключение в catch (...) и в обработчике завершения.
Klaim
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 :: terminate - это абстракция более высокого уровня: она вызывается (либо во время выполнения, либо вами), чтобы указать, что в программе произошла ошибка и что по какой-то причине ее невозможно обработать, выбрасывая исключение. Необходимость в этом обычно возникает, когда ошибка возникает в самом механизме исключения, но вы можете использовать ее в любое время, когда вы не хотите, чтобы ваша программа продолжалась после указанной ошибки. В своем посте я составил полный список ситуаций, когда вызывается std :: terminate. Не указано, что делает std :: terminate, потому что вы контролируете это. Вы можете настроить поведение, зарегистрировав любые функции. Ограничения, которые у вас есть, заключаются в том, что функция не может вернуться на сайт ошибки и не может выйти через исключение, но технически вы даже можете запустить свой насос сообщений внутри. Список полезных вещей, которые можно делать внутри, см. В другом моем посте .

В частности, обратите внимание, что std :: terminate считается обработчиком исключений в контекстах, где std :: terminate вызывается из-за сгенерированного исключения, которое не может быть обработано, и вы можете проверить, что это за исключение, и проверить его с помощью C ++ 11 с использованием std :: rethrow_exception и std :: current_exception. Все это в моем посте .

Andrzej
источник
Желательно ли иметь обработчик очистки на случай, если программа завершится из-за системных сигналов? Например, недействительный доступ к памяти приводит к генерации сигнала SIGSEGV. В этом случае, хорошо ли просто позволить программе завершить работу и получить основной файл или зарегистрировать обработчик сигнала для выполнения очистки? Есть ли проблемы с очисткой при обработке системных сигналов по сравнению с очисткой при обработке std :: terminate?
kartik trivikram
12

quick_exit () !

Если ваша программа многопоточная, то вызов exit(), скорее всего, приведет к сбою, потому что глобальные / статические std::threadобъекты будут пытаться уничтожить без выхода из своих потоков.

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

Примечание: quick_exit () не поддерживается MSVC ++ до версии 2015.

Серж Рогач
источник
4
  • terminate оставляет вам возможность зарегистрировать, что произойдет при его вызове. Должен быть один из двух других.
  • exit - это обычный выход, позволяющий указать статус выхода. Обработчики, зарегистрированные at_exit (), запускаются
  • abort - это аварийный выход. Единственное, что запускается, - это обработчик сигнала для SIGABRT.
Программист
источник
4
  • terminate () вызывается автоматически, когда возникает исключение, которое не может быть обработано. По умолчанию terminate () вызывает abort (). Вы можете установить собственный дескриптор с помощью функции set_terminate ().

    abort () отправляет сигнал SIGABRT.

    exit () - не обязательно плохо. Он успешно закрывает приложение и вызывает функции atexit () в порядке LIFO. Обычно я не вижу этого в приложениях C ++, однако я вижу это во многих приложениях на основе unix, где в конце он отправляет код выхода. Обычно выход (0) указывает на успешный запуск приложения.

Даниэль
источник
8
Неудачно! И в Unix, и в DOS exit (0) указывает на успех, а любое другое значение, переданное в exit (), указывает на неудачу, а не наоборот!
Ричард Баррелл