Signaler et gérer les situations exceptionnelles : les exceptions#
Occasionnellement, un programme, ou plus généralement une fonction, peut rencontrer une situation exceptionnelle : c’est-à-dire une situation qui n’est pas prévue dans les entrées normales. Que faire dans ce cas? Continuer comme si de rien était pourrait amener toutes sortes de catastrophes. Renvoyer une valeur arbitraire risquerait de passer inaperçu par l’utilisateur du programme ou de la fonction.
On souhaite donc arrêter l’exécution de la fonction, en signalant qu’une situation exceptionnelle s’est produite.
Exemple : gestion d’entrées invalides#
Nous avons vu précédemment la fonction factorielle récursive :
int factorielle(int n) {
if ( n == 0 ) return 1;
return n * factorielle(n-1);
}
factorielle(4)
Que se passerait-il si on calculait factorielle(-1)
?
Cela
appellerait factorielle(-2)
qui à son tour appellerait
factorielle(-3)
, et ainsi de suite. Jusqu’à ce que le programme
plante. Allez-y, essayez pour voir ci-dessus. Et tenez-vous prêt à
redémarrer le noyau!
L’entrée -1
n’est pas prévue par la fonction. C’est une situation
exceptionnelle que l’on souhaiterait signaler immédiatement et de
façon plus informative que par un simple plantage.
#include <stdexcept>
using namespace std;
int factorielle(int n) {
if ( n < 0 ) throw invalid_argument("n doit être positif");
if ( n == 0 ) return 1;
return n * factorielle(n-1);
}
factorielle(4)
factorielle(-1)
Maintenant, l’utilisateur est satisfait : l’exécution
de factorielle
s’est immédiatement arrêté, avec un
message d’erreur explicite sur le problème rencontré.
Nous allons maintenant expliquer les différents
éléments utilisés : throw
(lancer), invalid_argument
et
la bibliothèque stdexcept
.
Signaler une exception#
Syntaxe
throw e;
Sémantique
Une situation exceptionnelle que je ne sais pas gérer s’est produite.
Je m’arrête immédiatement et je préviens mon «chef» (l’utilisateur ou la fonction appelante).
On dit qu’on signale une exception.
La situation est décrite par l’exception
e
e
est un objet quelconque; par exemple une exception standard.Si à son tour mon «chef» ne sait pas gérer, il prévient son «chef».
…
Si personne ne sait gérer, le programme s’arrête.
Quelques exceptions standard#
Les erreurs dans les programmes peuvent être classifiée en plusieurs types classiques; ci-dessus, il s’agissait d’un «argument invalide». Dans d’autre cas, cela peut être un problème d’allocation, une erreur arithmétique, etc. Cette classification aide le lecteur et l’utilisateur à mieux analyser les erreurs et, par exemple, décider du comportement à adopter face à l’une d’elle.
Pour cela, la bibliothèque standard stdexcept
fournit plusieurs
types d”
exceptions standard qui
forment une hiérarchie avec, tout en haut, le type le moins spécifique
exception
.
Quelques exceptions standard
exception
runtime_error
invalid_argument
,out_of_range
,length_error
logic_error
,bad_alloc
,system_error
…
Exercice
Essayez de deviner le rôle de chacune des exceptions standard ci-dessus
Gestion d’exception#
Exemple 1 : ouvrir accidentellement un fichier inexistant depuis une application#
Imaginez que vous utilisiez votre traitement de texte, et que vous lui demandiez d’ouvrir un fichier qui n’existe pas. Pour la fonction en charge d’ouvrir ce fichier, c’est une situation exceptionnelle. Pour autant, serait-il acceptable que le traitement de texte plante avec juste un petit message «fichier inexistant»?
Non : pour un traitement de texte, ce n’est pas une situation exceptionnelle. Il sait gérer : prévenir l’utilisateur que le fichier n’existe pas, puis continuer normalement.
Une situation peut-être exceptionnelle pour une fonction, sans qu’elle le soit pour son «chef».
Il nous faut donc un mécanisme par lequel le «chef» puisse prendre la main et gérer la situation.
Exemple 2#
Nous allons considérer un scénario similaire, en plus simple : un programme qui demande un nombre \(n\) à l’utilisateur et affiche la factorielle de ce nombre. Pour ce programme, ce n’est pas exceptionnel que l’utilisateur fasse des bêtises en saisissant une valeur invalide. Le programme sait gérer, en affichant un petit message avant de continuer.
Voilà comment cela s’écrit. Pour que tout soit au même endroit, nous rappelons d’abord la définition de la fonction factorielle.
#include <stdexcept>
using namespace std;
int factorielle(int n) {
if ( n < 0 ) throw invalid_argument("n doit être positif");
if ( n == 0 ) return 1;
return n * factorielle(n-1);
}
#include <iostream>
int n;
cout << "Veuillez saisir un nombre entier positif:" << endl;
cin >> n;
try {
int f = factorielle(n);
cout << "Factorielle n vaut " << f << endl;
} catch (invalid_argument & e) {
cout << "Valeur de n invalide: " << n << endl;
}
cout << "Je continue ..." << endl;
Les deux éléments nouveaux sont try
(essayer) et catch
(rattraper l’erreur).
En voici la signification.
Gestion des exceptions ♣#
Syntaxe
try {
bloc d instructions;
} catch (type & e) {
bloc d instructions;
}
Sémantique
Exécute le premier bloc d’instructions
Si le bloc signale une exception de type
type
, ce n’est pas grave, je sais gérer :L’exécution du premier bloc d’instruction s’interrompt
Le deuxième bloc d’instructions est exécuté
Résumé#
Nous avons vu comment signaler une situation exceptionnelle (communément appellée erreur) lors de l’exécution d’un programme ou d’une fonction.
Cela se fait avec
throw e
oùe
est une exception. Cette dernière est typiquement construite avec l’un des types d’exceptions de la bibliothèque standardstdexcept
.
Les fonctions appelantes peuvent alors gérer cette exception, à l’aide de
try ... catch ...
. Si aucune d’entre elles ne gère l’exception, alors le programme s’arrête.
Il y aurait bien d’autres choses à dire sur les exceptions, mais cela sera suffisant pour notre usage ce semestre.