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(...){return1;// or whatever}}
@ 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.
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.
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.
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 .
É 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?
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.
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.
std::abort
é razoável se uma exceção não puder ser resolvida em um destruidor.std::terminate
consulte esses artigos no excelente blog C ++ de Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminateRespostas:
Meu conselho seria não usar nenhum deles. Em vez disso,
catch
as exceções com as quais você não pode lidarmain()
e simplesmentereturn
de 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:fonte
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
abort
em 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ê podeabort
se 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
exit
receber 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.exit
também opcionalmente chama manipuladores antes de terminar o programa. Eles são registrados com as funçõesatexit
eon_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 pelastd::set_terminate
função, que por padrão simplesmente chamaabort
.Em C ++, você geralmente deseja evitar a chamada
abort
ouexit
com 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ãoexit
para 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 emmain
.std::terminate
deve ser considerada uma ferramenta de relatório de erros de última hora, mesmo em C ++. O problemastd::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 umtry { } catch (std::exception& ex) { }
bloco. Então, pelo menos, você pode relatar mais informações sobre exceções derivadas destd::exception
(embora, é claro, as exceções que não derivam destd::exception
ainda não sejam tratadas).Encapsular o corpo de
main
emtry { } 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.fonte
std::current_exception()
. Veja o exemplo aqui: akrzemi1.wordpress.com/2011/10/05/using-stdterminatestd :: 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 .
fonte
quick_exit () !
Se o seu programa for multi-threaded, então a chamada
exit()
provavelmente resultará em um travamento porquestd::thread
objetos 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()
oustd::terminate()
pode ser chamado.Nota: quick_exit () não é compatível com MSVC ++ até a versão 2015.
fonte
fonte
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.
fonte