abandonner, terminer ou quitter?

112

Quelle est la différence entre ces trois et comment dois-je terminer le programme en cas d'exception que je ne peux pas gérer correctement?

Il n'y a rien que nous puissions faire
la source
3
Ce n'est pas un doublon, mais plutôt un sous-ensemble avec quelques bonnes réponses stackoverflow.com/questions/397075/... et il a également été étiqueté C ++!
Ellie Kesselman
std::abortest raisonnable si une exception ne peut pas être résolue dans un destructeur.
Daniel le
1
Pour plus d'informations sur std::terminateces articles, consultez l'excellent blog C ++ d'Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Réponses:

3

Mon conseil serait de ne pas en utiliser. Au lieu de cela, catchles exceptions que vous ne pouvez pas gérer main()et simplement à returnpartir de là. Cela signifie que vous avez la garantie que le déroulement de la pile se déroule correctement et que tous les destructeurs sont appelés. En d'autres termes:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}
SS Anne
la source
8
@Neil: D'accord, mais les exceptions que le programme ne peut pas gérer doivent être signalées et relancées. Laissez l'application planter.
John Dibling
13
Pour vous assurer que la pile se déroule, vous devez toujours attraper en main. Mais je relancerais de la prise. Comme certains OS ont la capacité d'appeler automatiquement l'infrastructure de débogage si vous avez compilé en débogage.
Martin York
5
Les exceptions qui ne sont pas détectées même par un gestionnaire de niveau supérieur peuvent appeler une fonction de rapport système qui vide le processus et télécharge le rapport d'exception à l'attention des développeurs, comme le rapport d'erreurs Windows, les rapports d'erreurs Mac OS X et les journaux d'erreurs des applications iPhone.
JBRWilkinson
6
@John La raison pour laquelle cela me surprend est que, bien que cela soit parfaitement logique à la lumière de la façon dont les exceptions sont réellement implémentées, cela brise l'abstraction selon laquelle une exception "se propage dans la pile" jusqu'à ce qu'un gestionnaire approprié soit trouvé (ou terminer est appelé). Et les abstractions qui fuient, bien que souvent inévitables, sont nécessairement surprenantes lorsqu'elles sont rencontrées.
Tyler McHenry
11
-1 parce que cela ne répond pas à la moitié de la question. "Quelle est la différence entre [abandonner, terminer ou quitter?]" C'est une meilleure réponse: stackoverflow.com/a/397081/353094 Aussi stackoverflow.com/a/2820407/353094 est une excellente réponse.
leetNightshade
149
  • abort indique une fin "anormale" du programme et déclenche le signal POSIX SIGABRT, ce qui signifie que tout gestionnaire que vous avez enregistré pour ce signal sera appelé, bien que le programme se terminera toujours après les mots dans les deux cas. Habituellement, vous utiliserez abortdans un programme C pour quitter un cas d'erreur inattendu où l'erreur est susceptible d'être un bogue dans le programme, plutôt que quelque chose comme une mauvaise entrée ou une panne du réseau. Par exemple, vous pouvez abortsi une structure de données contient un pointeur NULL alors que cela ne devrait logiquement jamais se produire.

  • exit indique une fin "normale" du programme, bien que cela puisse toujours indiquer un échec (mais pas un bogue). En d'autres termes, vous pourriez exitavec un code d'erreur si l'utilisateur a donné une entrée qui n'a pas pu être analysée ou si un fichier n'a pas pu être lu. Un code de sortie de 0 indique le succès. exitappelle également éventuellement des gestionnaires avant de terminer le programme. Ceux-ci sont enregistrés avec les fonctions atexitet on_exit.

  • std :: terminate est ce qui est automatiquement appelé dans un programme C ++ lorsqu'il y a une exception non gérée. C'est essentiellement l'équivalent C ++ de abort, en supposant que vous signalez toutes vos erreurs exceptionnelles au moyen de la levée d'exceptions. Cela appelle un gestionnaire défini par la std::set_terminatefonction, qui par défaut appelle simplement abort.

En C ++, vous voulez généralement éviter d'appeler abortou exiten cas d'erreur, car il vaut mieux lever une exception et laisser le code plus haut dans la pile d'appels décider si la fin du programme est appropriée ou non. Que vous l'utilisiez ou non exitpour réussir est une question de circonstance - qu'il soit logique ou non de terminer le programme ailleurs que dans l'instruction return main.

std::terminatedevrait être considéré comme un outil de rapport d'erreurs de dernière minute, même en C ++. Le problème avec std::terminateest que le gestionnaire de terminate n'a pas accès à l'exception qui n'a pas été gérée, il n'y a donc aucun moyen de savoir ce que c'était. Il est généralement préférable d'emballer l'intégralité de main dans un try { } catch (std::exception& ex) { }bloc. Au moins, vous pouvez signaler plus d'informations sur les exceptions dérivées de std::exception(bien que, bien sûr, les exceptions qui ne dérivent pas de celles- std::exceptionci ne soient toujours pas gérées).

L'encapsulation du corps de mainin try { } catch(...) { }n'est pas beaucoup mieux que de définir un gestionnaire de terminaison, car encore une fois, vous n'avez pas accès à l'exception en question. Edit: Selon la réponse de Neil Butterworth, il y a un avantage dans le fait que la pile est déroulée dans ce cas, ce qui n'est (quelque peu surprenant) pas vrai pour une exception non gérée.

Tyler McHenry
la source
10
Pouvez-vous mettre à jour cette réponse avec des informations C ++ 11? Il semble qu'il existe maintenant des moyens d'obtenir l'exception dans le gestionnaire catch (...) et terminate.
Klaim
1
En C ++ la fin gestionnaire n'avoir accès à l'exception par . Voir l'exemple ici: akrzemi1.wordpress.com/2011/10/05/using-stdterminatestd::current_exception()
anorm
Peu importe que vous puissiez obtenir l'exception actuelle, car vous ne pouvez pas l'inspecter. Tout ce que vous pouvez faire est de le relancer.
seattlecpp
2
@seattlecpp, vous pouvez le relancer et en attraper une référence, que vous pouvez ensuite inspecter
gpeche
16

std :: abort et std :: exit (et plus encore: std :: _ Exit, std :: quick_exit) ne sont que des fonctions de niveau inférieur. Vous les utilisez pour dire au programme ce que vous voulez qu'il fasse exactement: quels destructeurs (et si) appeler, quelles autres fonctions de nettoyage appeler, quelle valeur renvoyer, etc.

std :: terminate est une abstraction de plus haut niveau: elle est appelée (par le run-time ou par vous) pour indiquer qu'une erreur s'est produite dans le programme et que, pour une raison quelconque, il n'est pas possible de gérer en lançant une exception. La nécessité de cela se produit généralement lorsqu'une erreur se produit dans le mécanisme d'exception lui-même, mais vous pouvez l'utiliser à tout moment lorsque vous ne souhaitez pas que votre programme continue au-delà de l'erreur donnée. J'ai compilé la liste complète des situations où std :: terminate est appelé dans mon message. Ce n'est pas spécifié ce que fait std :: terminate, car vous en êtes le contrôle. Vous pouvez configurer le comportement en enregistrant toutes les fonctions. Les limitations que vous avez sont que la fonction ne peut pas retourner au site d'erreur et qu'elle ne peut pas quitter via une exception, mais techniquement, vous pouvez même démarrer votre pompe de message à l'intérieur. Pour la liste des choses utiles que vous pouvez faire à l'intérieur, consultez mon autre article .

En particulier, notez que std :: terminate est considéré comme un gestionnaire d'exceptions dans les contextes où std :: terminate est appelé en raison d'une exception levée qui n'a pas pu être gérée, et vous pouvez vérifier ce qu'était l'exception et l'inspecter en utilisant C ++ 11 en utilisant std :: rethrow_exception et std :: current_exception. Tout est dans mon post .

Andrzej
la source
Est-il conseillé d'avoir un gestionnaire de nettoyage au cas où le programme se termine en raison de signaux système? Par exemple, un accès mémoire invalide conduit à la génération d'un signal SIGSEGV. Dans ce cas, est-il bon de laisser simplement le programme se terminer et d'avoir le fichier de base ou d'enregistrer un gestionnaire de signaux pour faire le nettoyage? Y a-t-il des problèmes de nettoyage tout en traitant les signaux système par rapport à un nettoyage en manipulant std :: terminate?
kartik trivikram
12

quick_exit () !

Si votre programme est multi-thread, alors l'appel exit()entraînera probablement un plantage car les std::threadobjets globaux / statiques seront tentés de se détruire sans quitter leurs threads.

Si vous souhaitez renvoyer un code d'erreur et quitter le programme (plus ou moins) normalement, appelez des quick_exit()programmes multithreads. Pour résiliation anormale (sans possibilité pour vous de spécifier le code d'erreur), abort()ou std::terminate()peut être appelé.

Remarque: quick_exit () n'est pas pris en charge par MSVC ++ jusqu'à la version 2015.

Serge Rogatch
la source
4
  • terminate vous laisse la possibilité d'enregistrer ce qui se passera quand il sera appelé. Devrait être l'un des deux autres.
  • exit est une sortie normale permettant de spécifier un état de sortie. Les gestionnaires enregistrés par at_exit () sont exécutés
  • abandonner est une sortie anormale. La seule chose qui est exécutée est le gestionnaire de signaux pour SIGABRT.
Programmeur
la source
4
  • terminate () est automatiquement appelée lorsqu'une exception se produit qui ne peut pas être gérée. Par défaut, terminate () appelle abort (). Vous pouvez définir un handle personnalisé avec la fonction set_terminate ().

    abort () envoie le signal SIGABRT.

    exit () n'est pas nécessairement une mauvaise chose. Il réussit à quitter l'application et appelle les fonctions atexit () dans l'ordre LIFO. Je ne vois normalement pas cela dans les applications C ++, cependant, je le vois dans de nombreuses applications basées sur unix où il envoie un code de sortie à la fin. Habituellement, un exit (0) indique une exécution réussie de l'application.

Daniel
la source
8
Infructueux! Sous Unix et DOS, exit (0) indique le succès et toute autre valeur passée à exit () indique un échec, et non l'inverse!
Richard Barrell