1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui...
-
Upload
johanne-fremont -
Category
Documents
-
view
109 -
download
0
Transcript of 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui...
1
La gestion des exceptions C++
Un programme peut rencontrer des “conditions exceptionnelles” qui risquent de compromettre la poursuite de son exécution
La détection d’un incident et son traitement dans les programmes importants doivent se faire dans des parties différentes du code.
Exemple trivial :int *pint;pint = new int;If (pint == NULL) {
cout << “Manque de mémoire pour pint” << endl;exit(1);
}
2
La gestion des exceptions C++
La solution moderne du problème : les exceptions.
Elles permettent le découplage total de la détection d’une anomalie (exception) de son traitement en s’affranchissant de la hiérarchie des appels.
Une exception est une rupture de séquence déclenchée par une instruction “throw” comportant une expression (objet) d’un type (classe) donné.
3
Gestion de l’exception (1)
La gestion de l’exception se fait en deux étapes :
Une erreur détectée va lever une exception (throw)
Un gestionnaire va récupérer cette exception et la traiter convenablement (catch)
Pour pouvoir détecter les exceptions, les instructions susceptibles de lever des exceptions sont incluses dans un bloc spécifique (try)
4
Gestion de l’exception (2) Définition de l’exception
class exception_triviale {…
}; Utilisation et levée de l’exception
…try {
int i = 0;i--;if (i < 0 || i > NMAX) {
exception_triviale e;throw(e);
}…
} Interception et traitement de l’exception
catch (exception_triviale e) {…
}
5
Utilisation d’un gestionnaire d’exception#include <iostream.h>typedef int Erreur;class vecteur {protected:
int *pespace;int taille;
public:vecteur (int _taille)
{taille=_taille;pespace=new int[taille];}//surcharge d'opérateur
d'adressage avec une fonction - membreint & operator[](int i);
};
int& vecteur::operator[](int i){if((i<0)||(i>=taille)) throw -1;return pespace[i];
}
int main(){try{
vecteur v(2);v[2]=1;
}catch(Erreur){
cerr<<"Indice invalide"<<endl;
}return 0;
}Resultat : Indice invalideCommentaires : c’est une exception avec le passage d’un type catch(Erreur), Erreur étant un type“Catch” peut être interprété comme le nom de la fonction – gestionnaire En fait le bloc catch contient la séquence de traitement correspondante
6
Gestionnaire avec passage d’une valeur#include <iostream.h>enum Erreurs {depasseG, depasseD};class vecteur {protected:
int *pespace;int taille;
public:vecteur (int _taille)
{taille=_taille;pespace=new int[taille];}//surcharge d'opérateur
d'adressage avec une fonction - membreint & operator[](int i);
};
int& vecteur::operator[](int i){if(i<0) throw depasseG;
if(i>=taille) throw depasseD;return pespace[i];
}
int main(){try{
vecteur v(2);v[2]=1;
}catch(Erreurs e){ if(e==depasseG)
cout<<"Indice trop petit"<<endl;
if(e==depasseD)cout<<"Indice trop grand"<<endl;
}return 0;
}Resultat : Indice trop grand
Commentaire : gestionnaire avec une valeur permet de gérér les exceptions plus finement
7
Gestion avec un objet-exceptionclass vecteur {protected:
int *pespace;int taille;
public:class Exception_portee{public : int indice; Exception_portee(int i)
{indice=i;}};vecteur(int_taille)
{taille=_taille; pespace=new int[taille];}
int & operator[](int i); };int& vecteur::operator[](int i){
if((i<0)||(i>=taille)) throw Exception_portee(i); return pespace[i];}
int main(){try{
vecteur v(2);v[2]=1;
}
catch(vecteur::Exception_portee e){ cerr<<"Erreur de
depassement dans le tableau i= "<<e.indice<<endl;
}return 0;
}Resultat : Erreur de depassement dans le tableau i= 2
Commentaire : gestionnaire avec une valeur permet de gérér les exceptions plus finement
8
Flot d’exécution (UML)
Vecteur v
Exception_portee e
Diagramme de séquence
9
Commentaires
Le modèle de gestion des exceptions proposé par C++ ne permet pas de reprendre l’exécution à partir de l’instruction ayant levé l’exception, mais le permet après le bloc catch correspondant
Si le bloc try/catch n’est pas prévu, l’exception déclenchée par throw provoque l’arrêt de l’exécution dans la fonction ou méthode courante.
10
Exemple avec exit#include <iostream.h>#include <stdlib.h>enum Erreurs {depasseG, depasseD};class vecteur {protected:
int *pespace;int taille;
public:vecteur (int _taille)
{taille=_taille;pespace=new int[taille];}
//surcharge d'opérateur d'adressage avec une fonction - membre
int & operator[](int i); };
int& vecteur::operator[](int i){if(i<0) throw depasseG;
if(i>=taille) throw depasseD;return pespace[i];
}
int main(){try{
vecteur v(2);v[2]=1;
}catch(Erreurs e){ if(e==depasseG)
cout<<"Indice trop petit"<<endl;
if(e==depasseD)cout<<"Indice trop grand"<<endl;
exit(-1);}return 0;
}
Commentaire : l’arrêt définitif du programme lors du traitement de l’exception
11
Exemple de plusieurs exceptions#include <iostream.h>#include <stdlib.h>class vecteur {protected:
int *pespace;int taille;
public:class Exception_creation{public :
int hors_indice;Exception_creation(int i){
hors_indice=i;}
};class Exception_limite{public : int indice; Exception_limite(int i){
indice=i;}};
vecteur (int _taille){if(_taille<=0) throw Exception_creation(_taille);
taille=_taille; pespace=new int[taille];}
int & operator[](int i); };// fin de la déclaration de la classe
int& vecteur::operator[](int i){if((i<0)||(i>=taille)){
Exception_limite e(i);
throw e;}
return pespace[i];}
12
Exemple de plusieurs exceptionsint main(){
try{vecteur v(-2);v[2]=1;
}
catch(vecteur::Exception_creation e){ cerr<<"Erreur de
depassement a la creation i= "<<e.hors_indice<<endl;
}catch (vecteur::Exception_limite e){
cerr<<"Erreur de depassement dans le tableau i= "<<e.indice<<endl;
exit(1);}return 0;
}
Quelle exception est “active”? Résultat? Erreur de dépassement à la création i = 2
Commentaires : les exceptions peuvent être déclanchées par n’importe quelle fonction car les définitions des classes sont connues.
13
Poursuite d’exécution du programme
Il est impossible de reprendre l’exécution du programme à l’instruction suivant le déclenchement de l’exception mais
Le flot de contrôle peut être repris après le bloc “catch”
Souvent le bloc try couvre toute une fonction de sorte qu’après l’exécution d’un gestionnaire d’exception ne provoquant pas d’arrêt , il y a retour de la fonction
14
Retour du flot de contrôle Exemple
int main(){try{
vecteur v(-2);v[2]=1;
}catch(vecteur::Exception_creation e){ cerr << "Erreur de dépassement à la création i="<< e.indice << endl;
}catch (vecteur::Exception_limite e){ cerr << "Erreur de dépassement dans le tableau i=“ << e.indice << endl;}cout << “Reprise après les exceptions" << endl;return 0;
}
Résultats :Erreur de dépassement à la création i=-2Reprise après les exceptions
15
Choix du gestionnaire
Le gestionnaire reçoit toujours une copie de l’expression passée à throw même s’il s’agit de la transmission par référence.Exemple : Exception_limite c;
throw c; // une copie d’objet c est crée. Lorsqu’une exception est transmise à un bloc
try, on recherche, dans les différents blocs catch associés, un gestionnaire approprié au type de l’expression mentionnée dans l’instruction throw.
Le mécanisme est le même que pour la recherche des fonctions-membres dans la hiérarchie des classes
16
Prise en compte des sorties des blocs Exemple :
void f(int n) { vecteur v1(5);
try{vecteur v2(5);v1[n]=1; //on ne sait pas si on est bien dans les
limites}catch (vecteur::Exception_limite e){ cerr << "Err de dépass dans le tableau i=“ << e.indice << endl;}cout << "Je reprends après les exceptions" << endl;// ici v1 est connu, v2 a été détruit
}
Le méchanisme de gestion des exceptions appelle le destructeur de tout objet automatique déjà construit et devenant hors de portée
17
Choix du gestionnaire Hiérarchie des classes d’exceptions
class Exception_vecteur {…};class Exception_creation : public Exception_vecteur {};class Exception_limite : public Exception_vecteur{};// ces classes sont déclarées à l’extérieur de la classe vecteur;void f() {…
throw Exception_creation; throw Exception_limite;
}
int main () {try {
…f();…
}catch (Exception_vecteur e) {
cout<< “Interception des deux ici car enfants de la classe Exception_vecteur” <<
endl;}
}
18
Le cheminement des exceptions
Quand une exception est levée par une fonction, on cherche tout d’abord un gestionnaire dans l’éventuel bloc try/catch associé à cette fonction
Si l’on n’en trouve pas, on poursuit la recherche dans un éventuel bloc try/catch associé à une fonction appelante
Si aucun gestionnaire d’exception n’est trouvé on appelle la fonction terminate . Par défaut terminate appelle la fonction abort.
19
Version 1int main() {
try{f1(); // dépassement de limite!
}catch (vecteur::Exception_limite e) { cerr<<"Dans main : Erreur de dépassement dans le tableau i="<<e.indice<<endl; }cout << “Reprise après les exceptions" << endl;return 0;
}void f1() {
try {vecteur v(10);v[10]=0; // Attention !vecteur v1(-1);
}catch (vecteur::Exception_creation e) {// est déclenchée par le constructeur
cout<<"Dans f1 : Exception_creation i= "<<e.hors_indice<<endl; }
}
Résultat :Dans main : Erreur de dépassement dans le tableau i=10Reprise après les exceptions
20
Version 2int main() { try { f1(); } catch (vecteur::Exception_limite e) { cerr<<"Dans main : Erreur de dépassement dans le tableau i= "<<e.indice<<endl; } cout << “Reprise après les exceptions" << endl; return 0;}void f1() { try{ vecteur v(10); v[9]=0; vecteur v1(-1); } catch (vecteur::Exception_creation e){ cout<<"Dans f1 : Exception_creation i= "<<e.hors_indice<<endl; }}
Résultat :Dans f1 : Exception_creation i= -1Reprise après les exceptions
21
Redéclenchement des excéptions (I)
Dans un gestionnaire , l’instruction throw (sans expression) retransmet l’exception au niveau englobant. catch(..){ … throw; }
Cette possibilité permet de compléter le traitement standard d’une exception par un traitement complémentaire spécifique
22
Redéclenchement des exceptions (II)
#include <iostream>#include <stdlib.h>using namespace std;void f();void main(void) {
try { f(n);}catch (int) { cout << « Exception
int dans main" << endl; exit(-1);
}cout << "suite bloc try du
main" << endl;}
void f(){ try { int n;
throw n; } catch (int) {
cout << « Exception int dans f" << endl; throw; }}//fin void f()Résultat : Exception int dans fException int dans main
23
Les exceptions standard C++
Les exceptions standard sont des classes dérivées d’une classe de base exception
Leur déclaration figure dans le fichier-entête stdexcept
Certaines peuvent être déclenchées par des fonctions ou des opérateurs de la bibliothèque standard
Example : bad_alloc : échec d’allocation mémoire par new
24
Spécification d’interface(I)
Une fonction peut spécifier les exceptions qu’elle est susceptible de déclencher.
Elle le fait à l’aide de throw placé juste après l’identificateur de la fonction
void f() throw (A,B){ …} // f est censée ne déclencher que les exceptions A et B
Toute exception non prévue et déclenchée à l’intérieur de la fonction (ou d’une fonction appelée) entraîne l’appel de la fonction «unexpected »
« unexpected » => terminate =>abort par défaut.
25
Spécification d’interface (II)
#include <iostream>using namespace std;void f(int) throw(int);void main(void) {
int n;cout << "Entier (0 a 2) : " ;
cin >> n;try {
f(n);}catch (int) {
cout << "exception int dans main" << endl;
}cout << "suite bloc try du
main" << endl;}
void f(int n) throw(int) {try { cout << "n = " << n << endl; switch (n) {
case 0 : { double d =
0; throw
d;//traité par f break;}
case 1 : { int n = 0; throw n;
//traité par main break;}
case 2 : { float f = 0; throw
f;//appel unexpected break;}
}}
catch (double) {cout << "exception double
dans f" << endl;}cout << "suite du bloc try dans f et retour
appelant" << endl;}
26
Spécification d’interface(III)void f(int n) throw(int) {
try { cout << "n = " << n << endl; switch (n) {
case 0 : { double d =
0; throw
d;//traité par f break;}
case 1 : { int n = 0; throw n;
//traité par main break;}
case 2 : { float f = 0; throw
f;//appel unexpected break;}
}}
catch (double) {cout << "exception double
dans f" << endl;}cout << "suite du bloc try dans f et retour
appelant" << endl;}
Résultats : Entier (0 à 2) : 0n=0exception double dans f
Entier (0 à 2) : 1n=1exception int dans main
Entier (0 à 2) : 2n=2Fin anormal ( exception std bad_exception)
27
Les exceptions standard C++
La bibliothèque stadard (stdl) comporte quelques classes qui dérivent d’un classe de base exception
Logic_errorDomain_errorInvalid_argumentLength_errorOut_of_range
Runtime_errorrange_erroroverflow_errorUnderflow_error
Bad_alloc //échec d’allocation mémoire par newBad_cast //échec de l’opérateur dynamic_castBad_exception //erreur de spécification d’exception (peut être déclenché par unexpected)Bad_typeid //echec de la fonction typeid
28
Les exceptions standard C++(2)#include <iostream>#include <stdexcept>using namespace std;int tableau[10] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
int f(int i);
void main(void) {try {
int n;cout << "Entier : " ;
cin >> n;int i = f(n);cout << "i = " << i
<< endl;}catch (exception& e) {
cout << "Exception : " << e.what() << endl;
}}
int f(int i) {if (i >= 0 && i <
(sizeof(tableau) / sizeof(int))) {return tableau[i];
}throw out_of_range("index
hors domaine");}
Résultats : Entier : -1Exception : index hors domaine Entier : 0i= 10
29
Création des exceptions dérivées de la classe Exception
Raisons
(1) Faciliter le traitement des exceptions : interception de toute exception avec le simple gestionnaire :
catch (exception & e){…}
Cela est vérifié pour les exceptions dérivées de la classe “exception”.
(2) Utiliser la fonction “what” de la classe exception, qui renvoie la chaîne de caractères utilisée par le constructeur de l’exception
30
Exemple de la fonction what#include <iostream.h>#include <stdlib.h>#include <stdexcept>
using namespace std;int main(){
try{throw range_error("anomalie_1");
}
catch (range_error &re){cout << "exception: “ << re.what() << endl;
}return 0;
}
Résultats :exception: anomalie_1
31
Un vrai exemple d’utilisation de « range_error » #include <iostream> #include <stdlib.h> #include <stdexcept>
using namespace std;
int main(){ try{ int i=100; double epsilon=0.1; double somme=0.0; for(int k=1; k<=100;k=k+10) { epsilon=epsilon/k; if (epsilon<=0.001) throw range_error("anomalie_1"); else {somme=somme+1/epsilon; cout<<"somme
courante"<<somme<<endl;} } cout<<"somme = "<<somme<<endl; }
catch (range_error &re){ cout<<"exception: "<<re.what()<<endl;
} }
somme courante10somme courante120exception: anomalie_1Press any key to continue
Résultat d’execution
32
Utilisation de l’exception Bad_Alloc #include "stdafx.h" #include <iostream> #include <stdlib.h> #include <stdexcept> #define MAX_NUMBER 1000000000 using namespace std;
int _tmain(int argc, _TCHAR* argv[]) {
try { int* ptrTab; for (int i=0;i<MAX_NUMBER; i++) ptrTab=new int[MAX_NUMBER]; } catch (bad_alloc & b) { cout<<"Exception d'allocation de
mémoire par new"<<endl; cout<<"Vous êtes trop gourmand!" <<endl<<b.what()<<endl; } return 0; }
Exception d'allocation de mémoire par newVous êtes trop gourmand!bad allocationPress any key to continue
l’exception « bad-alloc » est déclenchée par la fonction « new » de la librairie standard.
33
Héritage de la classe exception#include <iostream>#include <stdexcept>using namespace std;class monException1 : public exception {public:
monException1() {} // Surcharge de la méthode virtuelle what
virtual const char* what() const { return "Mon exception no 1"; }};class monException2 : public exception {public:
monException2(char* texte) { this->texte = texte; }
// Surcharge de la méthode virtuelle what
virtual const char* what() const { return texte; }private:
char* texte;};
void main(void) {
try { cout << "Bloc try 1" <<
endl; throw monException1();}catch (exception& e) { cout << "Exception : " <<
e.what() << endl;}
try { cout << "Bloc try 2" << endl; throw
monException2("deuxieme type");}catch (exception& e) { cout << "Exception : " <<
e.what() << endl;}
}