123 votes

Abandonner, terminer ou quitter ?

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

3 votes

Ce n'est pas un doublon, mais plutôt un sous-ensemble avec quelques bonnes réponses stackoverflow.com/questions/397075/… et il était aussi marqué C++!

0 votes

std::abort est raisonnable si une exception ne peut pas être résolue dans un destructeur.

1 votes

Pour plus d'informations sur std::terminate, consultez ces articles du blog C++ excellent d'Andrzej : akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate

171voto

Tyler McHenry Points 35551
  • arrêt 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 invoqué, même si le programme se terminera toujours par la suite dans tous les cas. Habituellement, vous utiliseriez la fonction abort dans un programme C pour sortir d'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 défaillance du réseau. Par exemple, vous pourriez appeler abort si une structure de données contenait un pointeur NULL alors que cela ne devrait logiquement jamais se produire.

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

  • std::terminate est ce qui est automatiquement appelé dans un programme C++ lorsqu'il y a une exception non gérée. Il s'agit essentiellement de l'équivalent C++ de abort, en supposant que vous rapportez toutes vos erreurs exceptionnelles en lançant des exceptions. Cela appelle un gestionnaire défini par la fonction std::set_terminate, qui appelle par défaut simplement abort.

En C++, vous voulez généralement éviter d'appeler abort ou exit en cas d'erreur, car il est préférable de lancer une exception et de laisser le code plus haut dans la pile d'appels décider si la fin du programme est appropriée ou non. Que vous utilisiez ou non exit pour le succès est une question de circonstance - que cela ait un sens ou non de terminer le programme ailleurs que à l'instruction de retour dans main.

std::terminate doit être considéré comme un outil de signalement d'erreur de dernier recours, même en C++. Le problème avec std::terminate est que le gestionnaire de fin n'a pas accès à l'exception non gérée, donc il n'y a aucun moyen de savoir ce que c'était. Vous êtes généralement bien mieux d'encadrer l'ensemble de main dans un bloc try { } catch (std::exception& ex) { }. Au moins, vous pouvez alors rapporter plus d'informations sur les exceptions qui dérivent de std::exception (bien sûr, les exceptions qui ne dérivent pas de std::exception se retrouveraient toujours non gérées).

Encadrer le corps de main dans un bloc try { } catch(...) { } n'est guère mieux que de définir un gestionnaire de fin, car une fois encore vous n'avez pas accès à l'exception en question. Il y a au moins un avantage, cependant : que le déroulement de la pile soit effectué lorsque une exception n'est pas du tout attrapée est défini par l'implémentation, donc si vous avez besoin d'un déroulement de pile garanti, ce serait une façon d'obtenir cela.

10 votes

Pouvez-vous mettre à jour cette réponse avec des informations sur C++11? Il semble qu'il existe maintenant des moyens d'obtenir l'exception dans le bloc catch (...) et dans le gestionnaire de terminaison.

2 votes

En C++, le gestionnaire de terminaison a accès à l'exception via std::current_exception(). Voir l'exemple ici: akrzemi1.wordpress.com/2011/10/05/using-stdterminate

0 votes

Il n'importe pas que vous puissiez obtenir l'exception actuelle, puisque vous ne pouvez pas l'inspecter. Tout ce que vous pouvez faire, c'est la relancer.

18voto

Andrzej Points 1388

Std::abort et std::exit (et plus: std::_Exit, std::quick_exit) ne sont que des fonctions de niveau inférieur. Vous les utilisez pour indiquer au programme ce que vous voulez précisément qu'il fasse : quels destructeurs (et si) appeler, quelles autres fonctions de nettoyage appeler, quelle valeur retourner, etc.

std::terminate est une abstraction de niveau supérieur : elle est appelée (soit par le runtime, soit 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 la gérer en lançant une exception. La nécessité de cela se produit généralement lorsque des erreurs surviennent dans le mécanisme d'exception lui-même, mais vous pouvez l'utiliser à tout moment lorsque vous ne voulez 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. Il n'est pas spécifié ce que std::terminate fait, car vous êtes en contrôle. Vous pouvez configurer le comportement en enregistrant n'importe quelles fonctions. Les limitations que vous avez sont que la fonction ne peut pas retourner au site d'erreur et ne peut pas sortir via une exception, mais techniquement vous pouvez même démarrer votre pompe de messages à l'intérieur. Pour la liste des choses utiles que vous pouvez faire à l'intérieur, voir mon autre message.

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 lancée qui n'a pas pu être gérée, et vous pouvez vérifier quelle était l'exception et l'inspecter en utilisant C++11 en utilisant std::rethrow_exception et std::current_exception. Tout est dans mon message.

0 votes

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 entraîne la génération du signal SIGSEGV. Dans ce cas, est-il bon de laisser simplement le programme se terminer et d'avoir le fichier core ou d'enregistrer un gestionnaire de signal pour faire le nettoyage? Y a-t-il des préoccupations concernant le nettoyage lors de la gestion de signaux système par rapport au nettoyage lors de la gestion de std::terminate?

17voto

Serge Rogatch Points 18

quick_exit() !

Si votre programme est multi-threadé, alors l'appel à exit() aboutira très probablement à un crash car les objets std::thread globaux/statiques seront tentés d'être détruits sans sortir de leurs threads.

Si vous voulez renvoyer un code d'erreur et quitter le programme (plus ou moins) normalement, appelez quick_exit() dans les programmes multi-threadés. Pour une terminaison anormale (sans possibilité pour vous de spécifier le code d'erreur), abort() ou std::terminate() peuvent être appelés.

Note : quick_exit() n'a pas été pris en charge par MSVC++ jusqu'à la version 2015.

6voto

Daniel Points 486
  • terminate() est automatiquement appelé lorsqu'une exception se produit et ne peut être gérée. Par défaut, terminate() appelle abort(). Vous pouvez définir une gestion personnalisée avec la fonction set_terminate().

    abort() envoie le signal SIGABRT.

    exit() n'est pas nécessairement une chose mauvaise. Il quitte avec succès l'application et appelle les fonctions atexit() dans l'ordre LIFO. Je ne vois pas souvent cela dans les applications C++, cependant, je le vois souvent dans de nombreuses applications basées sur Unix où il envoie un code de sortie à la fin. En général, exit(0) indique une exécution réussie de l'application.

8 votes

Échec! Dans Unix et DOS, exit(0) indique le succès et toute autre valeur passée à exit() indique l'échec, et non l'inverse!

4voto

AProgrammer Points 31212
  • terminer vous laisse la possibilité d'enregistrer ce qui se passera lorsque cela est appelé. Doit être l'un des deux autres.
  • sortie est une sortie normale permettant de spécifier un statut de sortie. Les gestionnaires enregistrés par at_exit() sont exécutés
  • abandonner est une sortie anormale. La seule chose exécutée est le gestionnaire de signal pour SIGABRT.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X