33
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); }

1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

Embed Size (px)

Citation preview

Page 1: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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);

}

Page 2: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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é.

Page 3: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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)

Page 4: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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) {…

}

Page 5: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 6: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 7: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 8: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

8

Flot d’exécution (UML)

Vecteur v

Exception_portee e

Diagramme de séquence

Page 9: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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.

Page 10: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 11: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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];}

Page 12: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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.

Page 13: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 14: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 15: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 16: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 17: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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;}

}

Page 18: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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.

Page 19: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 20: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 21: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 22: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 23: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 24: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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.

Page 25: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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;}

Page 26: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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)

Page 27: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 28: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 29: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 30: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 31: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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

Page 32: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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.

Page 33: 1 La gestion des exceptions C++ Un programme peut rencontrer des conditions exceptionnelles qui risquent de compromettre la poursuite de son exécution

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;}

}