J'aimerais que mon code C++ cesse de fonctionner si une certaine condition est remplie, mais je ne sais pas comment faire. Donc, à tout moment, si une instruction if
est vraie, terminez le code comme suit:
if (x==1)
{
kill code;
}
Il existe plusieurs manières, mais vous devez d’abord comprendre pourquoi le nettoyage d’objet est important, et donc la raison std::exit
est marginalisé par les programmeurs C++.
C++ utilise un idiome appelé RAII , qui signifie simplement que les objets doivent effectuer l'initialisation dans le constructeur et le nettoyage dans le destructeur. Par exemple, la classe std::ofstream
[peut] ouvrir le fichier au cours du constructeur, puis l'utilisateur effectue des opérations de sortie sur celui-ci, puis à la fin de son cycle de vie, généralement déterminé par son étendue. , le destructeur est appelé qui ferme essentiellement le fichier et vide tout contenu écrit sur le disque.
Que se passe-t-il si vous n'obtenez pas le destructeur pour vider et fermer le fichier? Qui sait! Mais il n'écrira peut-être pas toutes les données qu'il était censé écrire dans le fichier.
Par exemple considérons ce code
_#include <fstream>
#include <exception>
#include <memory>
void inner_mad()
{
throw std::exception();
}
void mad()
{
std::unique_ptr<int> ptr(new int);
inner_mad();
}
int main()
{
std::ofstream os("file.txt");
os << "Content!!!";
int possibility = /* either 1, 2, 3 or 4 */;
if(possibility == 1)
return 0;
else if(possibility == 2)
throw std::exception();
else if(possibility == 3)
mad();
else if(possibility == 4)
exit(0);
}
_
Ce qui se passe dans chaque possibilité est:
os
, appelant ainsi son destructeur et effectuant un nettoyage approprié par fermer et rincer le fichier sur le disque.inner_mad
_, le dérouleur passera par la pile de mad
et main
pour effectuer un nettoyage approprié, tous les objets vont être détruits correctement, y compris ptr
et os
.exit
est une fonction C qui n’est ni consciente ni compatible avec les idiomes C++. Il n'effectue pas de nettoyage de vos objets, y compris os
dans la même étendue. Donc, votre fichier ne sera pas fermé correctement et pour cette raison, le contenu pourrait ne jamais y être écrit!return 0
_ et ayant ainsi le même effet que la possibilité 1, c'est-à-dire nettoyer.Mais ne soyez pas si sûr de ce que je viens de vous dire (principalement les possibilités 2 et 3); continuez à lire et nous découvrirons comment effectuer un nettoyage approprié basé sur une exception.
Vous devriez le faire autant que possible; préférez toujours revenir de votre programme en renvoyant un état de sortie correct de main.
L'appelant de votre programme, et éventuellement le système d'exploitation, voudra peut-être savoir si ce que votre programme était censé faire a été fait avec succès ou non. Pour cette même raison, vous devez renvoyer zéro ou EXIT_SUCCESS
pour signaler que le programme s'est terminé avec succès et EXIT_FAILURE
pour signaler le programme terminé sans succès, any toute autre forme de valeur de retour est définie par la mise en œuvre (§18.5/8).
Cependant, vous risquez de perdre beaucoup de temps dans la pile d’appels, et le renvoi de tout cela peut être douloureux ...
Le lancement d'une exception effectuera un nettoyage d'objet approprié à l'aide du dépilage de pile, en appelant le destructeur de chaque objet de la portée précédente.
Mais voici la prise ! Son implémentation est définie si le déroulement de la pile est effectué lorsqu'une exception levée n'est pas gérée (par la clause catch (...)] ou même si vous avez une fonction noexcept
dans le milieu de la pile d'appels. Ceci est indiqué dans §15.5.1 [except.terminate]:
Dans certaines situations, la gestion des exceptions doit être abandonnée pour des techniques de traitement des erreurs moins subtiles. [Note: Ces situations sont:
[...]
- lorsque le mécanisme de traitement des exceptions ne parvient pas à trouver un gestionnaire pour une exception levée (15.3) ou lorsque la recherche d'un gestionnaire (15.3) rencontre le bloc le plus externe d'une fonction avec une spécification
noexcept
qui ne permet pas l'exception (15.4), ou [...][...]
Dans de tels cas, std :: terminate () est appelé (18.8.3). Dans la situation où aucun gestionnaire correspondant n'est trouvé, il est défini par l'implémentation que la pile soit ou non déroulée avant l'appel de std :: terminate () [...]
Nous devons donc l'attraper!
Etant donné que les exceptions non capturées ne peuvent pas effectuer le déroulement de la pile (et par conséquent ne procéderont pas au nettoyage correct), nous devrions intercepter l'exception dans main, puis renvoyer un statut de sortie ( EXIT_SUCCESS
ou EXIT_FAILURE
).
Donc, une bonne installation serait:
_int main()
{
/* ... */
try
{
// Insert code that will return by throwing a exception.
}
catch(const std::exception&) // Consider using a custom exception type for intentional
{ // throws. A good idea might be a `return_exception`.
return EXIT_FAILURE;
}
/* ... */
}
_
Cela n'effectue aucune sorte de déroulement de la pile et aucun objet actif de la pile n'appelle son destructeur respectif pour effectuer le nettoyage.
Ceci est appliqué dans §3.6.1/4 [basic.start.init]:
Terminer le programme sans quitter le bloc actuel (par exemple, en appelant la fonction std :: exit (int) (18.5)) ne détruit aucun objet avec une durée de stockage automatique (12.4) . Si std :: exit est appelé pour mettre fin à un programme lors de la destruction d'un objet avec une durée de stockage statique ou de thread, le comportement du programme n'est pas défini.
Pensez-y maintenant, pourquoi voudriez-vous faire une chose pareille? Combien d'objets avez-vous douloureusement endommagé?
Il existe d'autres moyens de terminer un programme (autre que le blocage), mais ils ne sont pas recommandés. Juste pour clarifier, ils vont être présentés ici. Remarquez comment fin normale du programme ne signifie pas que la pile se déroule, mais un état okay pour le système d'exploitation .
std::_Exit
provoque une interruption normale du programme, et c'est tout.std::quick_exit
provoque une interruption normale du programme et appelle std::at_quick_exit
gestionnaires, aucun autre nettoyage n'est effectué.std::exit
provoque une interruption normale du programme, puis appelle std::atexit
gestionnaires. D'autres types de nettoyage sont effectués, tels que l'appel de destructeurs d'objets statiques.std::abort
provoque un arrêt anormal du programme, aucun nettoyage n'est effectué. Ceci devrait être appelé si le programme s'est terminé d'une manière vraiment, vraiment inattendue. Cela ne fera que signaler à l'OS la fin anormale. Certains systèmes effectuent un vidage mémoire dans ce cas.std::terminate
appelle le std::terminate_handler
qui appelle std::abort
par défaut.Comme Martin York l'a mentionné, la sortie n'effectue pas le nettoyage nécessaire, contrairement au retour.
Il est toujours préférable d'utiliser return dans le lieu de sortie. Au cas où vous ne seriez pas dans la rue principale, retournez-y en premier.
Prenons l'exemple ci-dessous. Avec le programme suivant, un fichier sera créé avec le contenu mentionné. Mais si return est commenté et non commenté exit (0), le compilateur ne vous assure pas que le fichier aura le texte requis.
int main()
{
ofstream os("out.txt");
os << "Hello, Can you see me!\n";
return(0);
//exit(0);
}
Pas seulement cela, avoir plusieurs points de sortie dans un programme rendra le débogage plus difficile. Utilisez exit uniquement lorsque cela peut être justifié.
Appelez la fonction std::exit
.
Les gens disent "appelez l'exit (code de retour)", mais c'est une mauvaise forme. Dans les petits programmes, ça va, mais cela pose un certain nombre de problèmes:
Vraiment, le seul moment où vous devriez sortir du problème est avec cette ligne dans main.cpp:
return 0;
Si vous utilisez exit () pour gérer les erreurs, vous devez en savoir plus sur les exceptions (et les exceptions d'imbrication), en tant que méthode beaucoup plus élégante et sûre.
return 0;
mettez cela où vous voulez dans int main()
et le programme se fermera immédiatement.
Si vous rencontrez une erreur au fond du code, émettez une exception ou définissez le code d'erreur. Il est toujours préférable de lancer une exception au lieu de définir des codes d'erreur.
Renvoyez une valeur de votre main
ou utilisez la fonction exit
. Les deux prennent un int. Quelle que soit la valeur renvoyée, la valeur renvoyée n'a pas vraiment d'importance.
Le programme se terminera lorsque le flux d'exécution atteindra la fin de la fonction principale.
Pour terminer avant cela, vous pouvez utiliser la fonction exit (int status), où status est une valeur renvoyée à celui qui a démarré le programme. 0 indique normalement un état sans erreur
En général, vous utiliseriez la méthode exit()
avec un état de sortie approprié .
Zéro signifie une course réussie. Un statut différent de zéro indique qu'un problème quelconque s'est produit. Ce code de sortie est utilisé par les processus parents (scripts Shell, par exemple) pour déterminer si un processus a été exécuté avec succès.
Au-delà de l'appel exit (code_erreur) - qui appelle des gestionnaires atexit, mais pas des destructeurs RAII, etc. - de plus en plus, j'utilise des exceptions.
De plus en plus mon programme principal ressemble à
int main(int argc, char** argv)
{
try {
exit( secondary_main(argc, argv );
}
catch(...) {
// optionally, print something like "unexpected or unknown exception caught by main"
exit(1);
}
}
où secondary_main se trouve dans où tous les éléments qui étaient à l'origine sont placés - c'est-à-dire que le principal d'origine est renommé secondary_main et que le talon principal ci-dessus est ajouté. Ceci est juste une finesse, de sorte qu'il n'y ait pas trop de code entre le plateau et la prise principale.
Si vous voulez, attrapez d'autres types d'exceptions.
J'aime bien capturer les types d'erreur de chaîne, comme std :: string ou char *, et les imprimer dans le gestionnaire de captures dans main.
L'utilisation d'exceptions comme celle-ci permet au moins d'appeler les destructeurs RAII, afin qu'ils puissent effectuer le nettoyage. Ce qui peut être agréable et utile.
Globalement, la gestion des erreurs C (sorties et signaux) et la gestion des erreurs C++ (exceptions try/catch/throw) s’exécutent au mieux, de manière incohérente.
Ensuite, où vous détectez une erreur
throw "error message"
ou un type d'exception plus spécifique.