Vous êtes sur la page 1sur 39

LOG2410 - Conception logicielle

Chapitre 16

Introduction aux exceptions

© François Guibault, 2005


Introduction aux exceptions

Sommaire
• Pourquoi les exceptions ?
• Quelques définitions.
• Quelques exemples sur une classe Vecteur.
• Les exceptions du standard.
• L’opérateur new et les exceptions.

16- 2
© François Guibault – 2005
Introduction aux exceptions

Pourquoi les exceptions ?


• Les traitement des exceptions est un aspect
difficile de la conception.
• Les méthodes conventionnelles de traitement
des erreurs peuvent facilement être ignorées
les programmeurs :
– Codes de retour de fonctions.
– Indicateurs statiques d’erreurs.
• Le traitement complet des erreurs par des
méthodes classiques complique énormément
la logique d’un programme.

16- 3
© François Guibault – 2005
Introduction aux exceptions

Pourquoi les exceptions ?


On veut:
• Fournir un mécanisme sûr de traitement des
erreurs.
• Fournir un mécanisme qui ne peut pas être
ignoré.
• Incorporer la notion d’erreur directement dans
le langage.
• Fournir une façon de traiter les erreurs qui
n’influence pas trop le déroulement normal du
programme.
16- 4
© François Guibault – 2005
Introduction aux exceptions

Le traitement des exceptions


• Pour être efficace, le traitement des
exceptions doit transparaître dans tout le
code.
• Les exceptions se comportent comme des
évènements asynchrones qui peuvent
survenir à tout moment.
• Les exceptions ne peuvent pas être ignorées.

16- 5
© François Guibault – 2005
Introduction aux exceptions

Le traitement des exceptions


• Une exception permet de transmettre de
l’information entre l’endroit où se produit une
erreur et l’endroit où elle sera traitée.
• Lors du lancement d’une exception, le cours
normal d’exécution est suspendu et la pile
des appels est remontée jusqu’à l’endroit où
cette erreur peut être traitée.
• Le code intermédiaire entre le lancement
d’une exception et son traitement doit être
conçu de façon à laisser passer l’exception
sans corruption.

16- 6
© François Guibault – 2005
Introduction aux exceptions

Quelques définitions
Exception:
Interruption de séquence permettant de traiter une erreur.
try:
Énoncé permettant de rassembler un ensemble d’instructions
susceptibles de lancer une exception.
throw:
Instruction permettant de lancer une exception. Cette instruction doit
être comprise dans un bloc try. Un objet est créé pour encapsuler
l’exception et il sera transmis à l’endroit traitant cette exception.
catch:
Identifie un bloc d’instructions pouvant recevoir et traiter une
exception. C’est à cet endroit que l’exception lancée pourra être
traitée.

16- 7
© François Guibault – 2005
Introduction aux exceptions

Exemple simple
try
{
[...]
throw UneErreur( “Une erreur s’est produite ...” );
}

catch( UneErreur& e )
{
cerr << “Traitement de l’erreur ” << e << endl;
[...]
}

// Poursuite des opérations normales.


cerr << “Exception ou pas, on continue !” << endl;
[...]

16- 8
© François Guibault – 2005
Introduction aux exceptions

Exemple: Classe Vecteur


template< class T >
class Vecteur
{
public:
Vecteur( int t );
~Vecteur( void );

T& operator[]( int i );


int getTaille( void ) const;

private:
/// Le vecteur de données.
T* data;

/// Taille du vecteur.


int taille;
};

16- 9
© François Guibault – 2005
Introduction aux exceptions

Exemple : Erreur à la création du vecteur


class ErreurVecteurCreation : public std::exception {
public:
ErreurVecteurCreation( int tailleInvalide );
virtual ~ErreurVecteurCreation() throw();
virtual const char * what () const throw ();
private:
std::string message;
};

ErreurVecteurCreation::ErreurVecteurCreation
( int tailleInvalide ) {
std::ostringstream msg;
msg << "Erreur lors de la creation du vecteur : «
<< " Taille invalide ( " << tailleInvalide << " )";
message = msg.str();
}
const char* ErreurVecteurCreation::what() const throw () {
return message.c_str();
}
16- 10
© François Guibault – 2005
Introduction aux exceptions

Exemple : Accès à un index invalide


class ErreurVecteurLimite : public std::exception {
public:
ErreurVecteurLimite( int index, int taille );
virtual ~ErreurVecteurLimite() throw();
virtual const char * what () const throw ();
private:
std::string message;
};

ErreurVecteurLimite::ErreurVecteurLimite( int index,


int taille ) {
std::ostringstream msg;
msg << "Erreur d'index dans le tableau : "
<< " Index invalide ( index=" << index
<< "; taille=" << taille << " )";
message = msg.str();
}
const char* ErreurVecteurLimite::what() const throw () {
return message.c_str();
} 16- 11
© François Guibault – 2005
Introduction aux exceptions

Exemple:
Implantation de la classe Vecteur
template<class T>
Vecteur<T>::Vecteur( int t )
: data( NULL ),
taille( t )
{
if ( taille < 0 )
{
throw ErreurVecteurCreation( taille );
}

data = new T[taille];


}

template<class T>
Vecteur<T>::~Vecteur( void )
{
delete [] data;
} 16- 12
© François Guibault – 2005
Introduction aux exceptions

Exemple:
Implantation de la classe Vecteur
template<class T>
T& Vecteur<T>::operator[]( int i )
{
if ( ( i<0 ) || ( i>=taille ) )
{
throw ErreurVecteurLimite( i, taille );
}

return data[ i ];
}

template<class T>
int Vecteur<T>::getTaille( void ) const
{
return taille;
}

16- 13
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 1
int main( int argc, char** argv ) {
try {
// Lance une exception de type ErreurVecteurCreation...
Vecteur<int> v( -5 );

// Lancerait une exception de type ErreurVecteurLimite, mais ...


v[10] = 10;
}

catch( ErreurVecteurLimite& err ) {


cerr << "ErreurVecteurLimite attrapee dans le main..." << endl;
cerr << err.what() << endl;
}

catch( ErreurVecteurCreation& err ) {


cerr << "ErreurVecteurCreation attrapee dans le main..." << endl;
cerr << err.what() << endl;
}

return 0;
}
16- 14
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 1
[selafa@d6128-02 chap16]$ ./test1

ErreurVecteurCreation attrapee dans le main...

Erreur lors de la creation du vecteur : Taille invalide ( -5 )

[selafa@d6128-02 chap16]$

16- 15
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 2
void fct( void ) {
try {
Vecteur<int> v( -5 );
cout << "À l'interieur de fct(), apres l'exception.\n";
}

catch( std::exception& err ) {


cerr << "Erreur detectee dans fct()... la fonction sera "
<< "interrompue et on retourne a l'appelant..." << endl;
cerr << err.what() << endl;
}
}

int main( int argc, char** argv ) {


cerr << "On appelle la fonction fct()" << endl;
fct();
cerr << "Retour au main() apres l'appel a fct()" << endl;

return 0;
}
16- 16
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 2
[selafa@d6128-02 chap16]$ ./test2

On appelle la fonction fct()

Erreur detectee dans fct()... la fonction sera interrompue et


on retourne a l'appelant...

Erreur lors de la creation du vecteur : Taille invalide ( -5 )

Retour au main() apres l'appel a fct()

[selafa@d6128-02 chap16]$

16- 17
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 3
void fct( void ) {
Vecteur<int> v( 10 );
v[15] = 3; // ErreurVecteurLimite
}

int main( int argc, char** argv ) {


try {
cerr << "On appelle la fonction fct()" << endl << endl;
fct();
cerr << "Retour au main() apres l'appel a fct()" << endl;
}

catch( std::exception& err ) {


cerr << endl << "Erreur detectee dans le main()... "
<< " le main() est interrompu ..." << endl;
cerr << err.what() << endl << endl;
}

return 0;
}
16- 18
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 3
[selafa@d6128-02 chap16]$ ./test3

On appelle la fonction fct()

Erreur detectee dans le main()... le main() est interrompu ...

Erreur d'index dans le tableau : Index invalide ( index=15;


taille=10 )

[selafa@d6128-02 chap16]$

16- 19
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 4
void autreExceptionMaison( void );
void terminaisonMaison( void );
void fct( void ) throw( ErreurVecteurLimite );

int main( int argc, char** argv ) {


// Remplace les fonction unexpected() et terminate().
set_unexpected( autreExceptionMaison );
set_terminate( terminaisonMaison );

try {
fct();
}
catch( ErreurVecteurLimite& err ) {
cerr << "ErreurVecteurLimite attrapee dans le main..." << endl;
cerr << err.what() << endl;
}
catch( ErreurVecteurCreation& err ) {
cerr << "ErreurVecteurCreation attrapee dans le main...";
cerr << err.what() << endl;
}
return 0;
16- 20
} © François Guibault – 2005
Introduction aux exceptions

Exemple : test 4
void fct( void ) throw( ErreurVecteurLimite )
{
Vecteur<int> v( -5 ); // Lance une ErreurVecteurCreation !!!
// Que se passera-t-il ?
v[10] = 12;
}

void autreExceptionMaison( void )


{
cerr << "Traitement maison d'une exception \"unexpected\"" << endl;
}

void terminaisonMaison( void )


{
cerr << "Terminaison maison du programme suite "
<< "a une exception inattendue" << endl;

cerr << "Astalavista Baby!!!!" << endl;

abort();
}
16- 21
© François Guibault – 2005
Introduction aux exceptions

Exemple : test 4
[selafa@d6128-02 chap16]$ ./test4

Traitement maison d'une exception "unexpected"

Terminaison maison du programme suite a une exception


inattendue

Astalavista Baby!!!!

Abandon

[selafa@d6128-02 chap16]$

16- 22
© François Guibault – 2005
Introduction aux exceptions

std::exception
• Le standard C++ prévoit une hiérarchie de
classes standard pour la gestion des exceptions.
• La classe racine de ces classes est la classe
exception.
– Cette classe spécifie une interface à laquelle se
conforment les différentes exceptions du standard.
– Cette classe possède une fonction virtuelle, what(),
qui retourne un pointeur à une chaîne de caractères
décrivant l’erreur.
– Chaque sous-classe surcharge cette méthode pour
afficher un message correspondant à l’erreur
survenue.

16- 23
© François Guibault – 2005
Introduction aux exceptions

std::exception et ses dérivées

exception

bad_alloc bad_cast bad_exception bad_typeid ios_base::failure

logic_error runtime_error

domain_error invalid_argument range_error overflow_error

length_error out_of_range underflow_error

16- 24
© François Guibault – 2005
Introduction aux exceptions

std::exception et ses dérivées


std::bad_alloc
Lancé par new si l’allocation de mémoire échoue.
std::bad_cast
Lancé par dynamic_cast si la conversion de type échoue.
std::bad_exception
Lancé par unexpected() en cas d’exception inattendue.
std::bad_typeid
Lancé par une expression de type typeid si le pointeur est
NULL.
std::ios_base::failure
Classe de base pour les exceptions lancées dans iostream.

16- 25
© François Guibault – 2005
Introduction aux exceptions

std::logic_error et ses dérivées


std::logic_error
Représente des problèmes dans la logique interne d’un programme
qui peuvent normalement être prévenus en écrivant correctement le
code de l’application.

Sous-classes de logic_error:
• std::invalid_argument. Indique que les arguments passés à la fonction
sont incorrects ou invalides
• std::length_error. Accès à hors de la plage de mémoire allouée.
Typiquement, débordement de tableau.
• std::domain_error. Représente des erreurs de domaine au sens
mathématique du terme.
• std::out_of_range. Représente un argument dont la valeur n’est pas
dans la plage attendue.
16- 26
© François Guibault – 2005
Introduction aux exceptions

std::runtime_error et ses dérivées


std::runtime_error
Représente des erreurs qui ne peuvent être détectées qu’à
l’exécution du programme.

Sous-classes de runtime_error
• std::overflow_error. Erreur arithmétique interne de
débordement.
• std::underflow_error. Erreur arithmétique interne de
dépassement de la capacité inférieure.
• std::range_error. Erreur de plage invalide dans les calculs
internes.

16- 27
© François Guibault – 2005
Introduction aux exceptions

L’opérateur new et les exceptions


• En C, les fonctions malloc(), realloc(), calloc()
et free() permettent d’allouer et de désallouer la
mémoire dynamiquement.
• Lorsqu’on fait un malloc(), par exemple, on peut
vérifier que tout s’est déroulé normalement en testant la
valeur de retour de l’appel à malloc(). Cette dernière
sera à NULL si l’allocation de la mémoire a échoué.
• En C++, l’opérateur new NE fonctionne PAS comme ça.
– new lance une exception de type std::bad_alloc si l’allocation a
échoué et aucune valeur n’est retournée par le new.
– int* tableau = new int[TAILLE];
– Si le new échoue, une exception sera lancée et le pointeur
tableau sera indéfini.
16- 28
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
Valeur de retour indéfini
// new1.cpp

unsigned long int TAILLE = 4294967295; // (2^32)-1

void main( void ){

double* ptrArray = new double[ TAILLE ];

if ( ptrArray == NULL )
{
cerr << "Le new a plante..." << endl;
cerr << "Astalavista baby" << endl;
exit( -1 );
}
else
{
cerr << "Le new a fonctionne..." << endl;
}
}
16- 29
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
Valeur de retour indéfini
[selafa@d6128-02 new]$ ./new1

Abandon

[selafa@d6128-02 new]$

16- 30
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
Lancement de l’exception std::bad_alloc
// new2.cpp

unsigned long int TAILLE = 4294967295; // (2^32)-1

void main( void ) {


// Le new est dans un bloc try.
try {
double* ptrArray = new double[ TAILLE ];
}

// Si le new a planté, il a lancé (throw) une exception.


catch ( bad_alloc& exception ) {
cout << "Le new a plante..." << endl;
cout << "Exception produite: " << exception.what() << endl;
terminate();
}

cout << "Suite du programme... comme l'exception appelle


terminate(), je serais surpris qu'on se rende ici !\n";
}
16- 31
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
lancement de l’exception std::bad_alloc
[selafa@d6128-02 new]$ ./new2

Le new a plante...

Exception produite: St9bad_alloc

Abandon

[selafa@d6128-02 new]$

16- 32
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
utilisation du type « nothrow »
// new3.cpp

unsigned long int TAILLE = 4294967295; // (2^32)-1

void main( void ) {

double* ptrArray = new( nothrow ) double[ TAILLE ];


if ( ptrArray == NULL )
{
cerr << "Le new a plante..." << endl;
abort();
}
else
{
cerr << "Le new a reussi..." << endl;
}

cout << "Suite du programme... comme le traitement de l'erreur


appelle abort(), je serais surpris qu'on se rende ici !\n";
}
16- 33
© François Guibault – 2005
Introduction aux exceptions
L’opérateur new et les exceptions :
utilisation du type « nothrow »
[selafa@d6128-02 new]$ ./new3

Le new a plante...

Abandon

[selafa@d6128-02 new]$

16- 34
© François Guibault – 2005
Introduction aux exceptions

L’opérateur new et les exceptions : ex.4


// new4.cpp

unsigned long int TAILLE = 4294967295; // (2^32)-1

void main( void )


{
// Essaie d'allouer beaucoup d'espace memoire.
double* ptrArray = new double[ TAILLE ];

// Le new a lancé une exception non traitée. Il y aura donc un


// appel à terminate() et on ne se rendra pas à la ligne
// suivante...
cout << "Je suis apres le new" << endl;
}

16- 35
© François Guibault – 2005
Introduction aux exceptions

L’opérateur new et les exceptions : ex.4

[selafa@d6128-02 new]$ ./new4

Abandon

[selafa@d6128-02 new]$

16- 36
© François Guibault – 2005
Introduction aux exceptions

L’opérateur new et les exceptions : ex.5


// new5.cpp

unsigned long int TAILLE = 4294967295; // (2^32)-1


void monGestionnaireNew( void );

void main( void ) {


set_new_handler( monGestionnaireNew );

double* ptrArray = new double[ TAILLE ];

cout << "Je suis apres le new" << endl;


}

void monGestionnaireNew( void ) {


cerr << "Je suis le gestionnaire responsable de gerer les
echecs de new..." << endl;

// Gestion minimale et radicale de l'erreur...


terminate();
}
16- 37
© François Guibault – 2005
Introduction aux exceptions

L’opérateur new et les exceptions : ex.5

[selafa@d6128-02 new]$ ./new5

Je suis le gestionnaire responsable de gerer les echecs de new…

Abandon

[selafa@d6128-02 new]$¨

16- 38
© François Guibault – 2005
Introduction aux exceptions
Traitement des exceptions :
quelle classe utiliser ?
• Un programmeur a trois choix quant à la
façon d’utiliser les exceptions. Il peut:
– Utiliser les classes existantes définies dans le
standard C++ (principalement std::logic_error et
std::runtime_error).
– Définir ses propres sous-classes d’une des
classes du standard C++.
– Définir sa propre hiérarchie de classes pour
représenter ses objets d’exceptions.
• Ce choix dépend généralement du contexte
du problème.
16- 39
© François Guibault – 2005