abortar, encerrar ou sair?

112

Qual é a diferença entre esses três, e como devo encerrar o programa em caso de exceção que não consigo tratar adequadamente?

Não há nada que possamos fazer
fonte
3
Este não é um duplicado, mas sim um subconjunto com algumas boas respostas stackoverflow.com/questions/397075/… e foi marcado como C ++ também!
Ellie Kesselman
std::aborté razoável se uma exceção não puder ser resolvida em um destruidor.
Daniel
1
Para obter mais informações, std::terminateconsulte esses artigos no excelente blog C ++ de Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Respostas:

3

Meu conselho seria não usar nenhum deles. Em vez disso, catchas exceções com as quais você não pode lidar main()e simplesmente returnde lá. Isso significa que você tem a garantia de que o desenrolamento da pilha ocorre corretamente e que todos os destruidores são chamados. Em outras palavras:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}
SS Anne
fonte
8
@ Neil: Basicamente concordado, mas as exceções que o programa não pode tratar devem ser relatadas e relançadas. Deixe o aplicativo travar.
John Dibling
13
Para garantir que a pilha seja desenrolada, você deve sempre capturar o principal. Mas eu voltaria a lançar da captura. Como alguns sistemas operacionais têm a capacidade de invocar automaticamente a infraestrutura de depuração se você compilou na depuração.
Martin York
5
Exceções que não são detectadas nem mesmo por um manipulador de nível superior podem invocar um recurso de relatório do sistema que despeja o processo e carrega o relatório de exceção para a atenção dos desenvolvedores, como Relatório de Erros do Windows, relatórios de erros do Mac OS X e logs de erros de aplicativos do iPhone.
JBR Wilkinson
6
@John A razão pela qual é surpreendente para mim é que, embora faça sentido à luz de como as exceções são realmente implementadas, ele quebra a abstração de que uma exceção "se propaga para cima na pilha" até que um manipulador adequado seja encontrado (ou finalizado seja chamado). E abstrações vazadas, embora muitas vezes inevitáveis, são necessariamente surpreendentes quando encontradas.
Tyler McHenry
11
-1 porque isso não responde a metade da pergunta. "Qual é a diferença entre [abortar, encerrar ou sair?]" Esta é uma resposta melhor: stackoverflow.com/a/397081/353094 Além disso, stackoverflow.com/a/2820407/353094 é uma ótima resposta.
leetNightshade
149
  • abort indica o fim "anormal" do programa e aumenta o sinal POSIX SIGABRT, o que significa que qualquer manipulador que você registrou para aquele sinal será invocado, embora o programa ainda termine após as palavras em ambos os casos. Normalmente, você usaria abortem um programa C para sair de um caso de erro inesperado em que o erro provavelmente fosse um bug no programa, em vez de algo como uma entrada incorreta ou uma falha de rede. Por exemplo, você pode abortse uma estrutura de dados tiver um ponteiro NULL, quando isso logicamente nunca deveria acontecer.

  • exit indica um final "normal" para o programa, embora isso ainda possa indicar uma falha (mas não um bug). Em outras palavras, você pode exitreceber um código de erro se o usuário fornecer uma entrada que não pode ser analisada ou um arquivo não pode ser lido. Um código de saída 0 indica sucesso. exittambém opcionalmente chama manipuladores antes de terminar o programa. Eles são registrados com as funções atexite on_exit.

  • std :: terminate é o que é chamado automaticamente em um programa C ++ quando há uma exceção não tratada. Isso é essencialmente o C ++ equivalente a abort, supondo que você esteja relatando todos os seus erros excepcionais por meio de exceções lançadas. Isso chama um manipulador que é definido pela std::set_terminatefunção, que por padrão simplesmente chama abort.

Em C ++, você geralmente deseja evitar a chamada abortou exitcom erro, já que é melhor lançar uma exceção e deixar o código mais acima na pilha de chamadas decidir se é ou não apropriado encerrar o programa. Se você usar ou não exitpara o sucesso é uma questão de circunstância - se faz ou não sentido terminar o programa em algum lugar diferente da instrução de retorno em main.

std::terminatedeve ser considerada uma ferramenta de relatório de erros de última hora, mesmo em C ++. O problema std::terminateé que o manipulador de terminação não tem acesso à exceção que não foi tratada, portanto, não há como saber o que era. Normalmente, é muito melhor envolver todo o main em um try { } catch (std::exception& ex) { }bloco. Então, pelo menos, você pode relatar mais informações sobre exceções derivadas de std::exception(embora, é claro, as exceções que não derivam de std::exceptionainda não sejam tratadas).

Encapsular o corpo de mainem try { } catch(...) { }não é muito melhor do que definir um manipulador de terminação, porque novamente você não tem acesso à exceção em questão. Edit: De acordo com a resposta de Neil Butterworth, há um benefício em que a pilha é desfeita neste caso, o que (um tanto surpreendentemente) não é verdadeiro para uma exceção não tratada.

Tyler McHenry
fonte
10
Você pode atualizar esta resposta com informações de C ++ 11? Parece que agora há maneiras de obter a exceção no catch (...) e no manipulador de terminação.
Klaim
1
Em C ++ o manipulador de terminar faz ter acesso à exceção através std::current_exception(). Veja o exemplo aqui: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm
Não importa se você pode obter a exceção atual, já que não pode inspecioná-la. Tudo que você pode fazer é jogá-lo novamente.
seattlecpp de
2
@seattlecpp você pode relançá-lo e pegar uma referência a ele, que você pode inspecionar
gpeche
16

std :: abort e std :: exit (e mais: std :: _ Exit, std :: quick_exit) são apenas funções de nível inferior. Você os usa para dizer ao programa o que você deseja fazer exatamente: quais destruidores (e se) chamar, quais outras funções de limpeza chamar, que valor retornar, etc.

std :: terminate é uma abstração de nível superior: é chamada (pelo tempo de execução ou por você) para indicar que ocorreu um erro no programa e que, por alguma razão, não é possível lidar com uma exceção. A necessidade disso normalmente ocorre quando ocorre um erro no próprio mecanismo de exceção, mas você pode usá-lo a qualquer momento quando não quiser que seu programa continue além do erro fornecido. Compilei a lista completa de situações em que std :: terminate é chamado em meu post. Não é especificado o que std :: terminate faz, porque você está no controle disso. Você pode configurar o comportamento registrando quaisquer funções. As limitações que você tem são que a função não pode retornar ao site do erro e não pode sair por meio de uma exceção, mas, tecnicamente, você pode até mesmo iniciar a bomba de mensagem interna. Para a lista de coisas úteis que você pode fazer por dentro, veja meu outro post .

Em particular, observe que std :: terminate é considerado um manipulador de exceção em contextos onde std :: terminate é chamado devido a uma exceção lançada que não pôde ser tratada, e você pode verificar qual era a exceção e inspecioná-la usando C ++ 11 usando std :: rethrow_exception e std :: current_exception. Está tudo na minha postagem .

Andrzej
fonte
É aconselhável ter um manipulador de limpeza caso o programa seja encerrado por causa de sinais do sistema? Por exemplo, o acesso inválido à memória leva à geração do sinal SIGSEGV. Neste caso, é bom apenas deixar o programa terminar e ter o arquivo principal ou registrar um manipulador de sinal para fazer a limpeza ?? Há alguma preocupação em fazer a limpeza ao manipular os sinais do sistema em comparação com fazer uma limpeza ao manipular std :: terminate?
kartik trivikram
12

quick_exit () !

Se o seu programa for multi-threaded, então a chamada exit()provavelmente resultará em um travamento porque std::threadobjetos globais / estáticos serão destruídos sem sair de seus threads.

Se você deseja retornar um código de erro e sair do programa (mais ou menos) normalmente, chame quick_exit()programas multi-threaded. Para finalização anormal (sem a possibilidade de você especificar o código de erro), abort()ou std::terminate()pode ser chamado.

Nota: quick_exit () não é compatível com MSVC ++ até a versão 2015.

Serge Rogatch
fonte
4
  • terminate deixa você a possibilidade de registrar o que acontecerá quando for chamado. Deve ser um dos outros dois.
  • exit é uma saída normal que permite especificar um status de saída. Manipuladores registrados por at_exit () são executados
  • abortar é uma saída anormal. A única coisa que é executada é o manipulador de sinal para SIGABRT.
AProgrammer
fonte
4
  • terminate () é chamado automaticamente quando ocorre uma exceção que não pode ser tratada. Por padrão, terminate () chama abort (). Você pode definir um identificador personalizado com a função set_terminate ().

    abort () envia o sinal SIGABRT.

    exit () não é necessariamente uma coisa ruim. Ele sai do aplicativo com êxito e chama as funções atexit () na ordem LIFO. Normalmente não vejo isso em aplicativos C ++, no entanto, vejo em muitos aplicativos baseados em Unix, onde envia um código de saída no final. Normalmente, uma saída (0) indica uma execução bem-sucedida do aplicativo.

Daniel
fonte
8
Mal sucedido! Tanto no Unix quanto no DOS, exit (0) indica sucesso e qualquer outro valor passado para exit () indica falha, não o contrário!
Richard Barrell