Polymorphisme
Gestion des exceptions
Programmation Orientée Objet en C++
Module: S5 - IFA et MIASI
Pr. Abdessamad EL BOUSHAKI
[Link]@[Link]
FST Marrakech
Université Cadi Ayyad
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 1/24
Polymorphisme
Gestion des exceptions
Polymorphisme
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 2/24
Polymorphisme
Gestion des exceptions
L’édition de lien et le polymorphisme
L’édition de lien est l’opération qui associe, à l’appel d’une fonction, l’adresse
du code de la fonction.
On distingue:
I La liaison statique (ou précoce): Quand une méthode à liaison statique est
appelée sur un objet, c’est la méthode correspondant à la classe de cet objet
déterminée au moment de la compilation qui est exécutée.
I La liaison dynamique (ou tardive) : Quand une méthode à liaison dynamique
est appelée sur un objet, c’est la méthode correspondant à la classe réelle de
cet objet déterminée au moment de l’exécution qui est exécutée.
Le polymorphisme est la capacité, pour une fonction, d’être interprétée
différemment en fonction de l’objet auquel elle s’applique.
On distingue:
I Le polymorphisme statique : c’est la surcharge. Le sens de la fonction est
déterminé statiquement par la signature des arguments à l’appel.
I Le polymorphisme dynamique : c’est le véritable polymorphisme. Le sens de
la fonction est déterminé par le type effectif de l’objet à l’appel.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 3/24
Polymorphisme
Gestion des exceptions
Résolution des liens
Une instance de sous-classe B est substituable à une instance de super-classe
A.
Que se passe-t-il lorsque B redéfinit une méthode de A ?
class A
{
public : void affiche () { cout < < " A " << endl ;}
};
class B : public A
{
public : void affiche () { cout < < " B " << endl ;}
};
int main ()
{
A aa ;
B bb ;
aa = bb ;
aa . affiche () ; // appel affiche () de A ? ou affiche () de B ?
}
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 4/24
Polymorphisme
Gestion des exceptions
Résolution des liens
En C++, c’est le type de la variable qui détermine la méthode à exécuter.
Résolution statique des liens.
int main ()
{
A aa ;
B bb ;
aa = bb ;
aa . affiche () ; // appel affiche () de A
}
Le choix de la fonction est conditionné par le type de aa connu au moment
de la compilation.
Puisque aa est un A alors il utilise la méthode A::affiche().
Il sera mieux que l’appel de méthode affiche() écrit B::affiche() ?
Puisque aa contient un B.
En POO, le programme ne peut déterminer l’adresse du code avant la
phase d’exécution, un autre mécanisme est donc nécessaire quand un
message est envoyé à un objet générique.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 5/24
Polymorphisme
Gestion des exceptions
Résolution dynamique des liens
Il pourrait dans certains cas sembler plus naturel de choisir la méthode
correspondante à la nature réelle de l’instance.
aa . affiche () ; -> bb . affiche () ;
Dans ces cas, il faut permettre la résolution dynamique des liens : Le
choix de la méthode à exécuter se fait à l’exécution, en fonction de la
nature réelles des instances.
Les langages orientés objet utilisent le concept d’association tardive. Quand
un objet reçoit un message, le code appelé n’est pas déterminé avant
l’exécution. Le compilateur s’assure que la fonction existe et vérifie le type
des arguments et de la valeur de retour, mais il ne sait pas exactement quel
est le code à exécuter.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 6/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
En C++, on indique au compilateur qu’une méthode peut faire l’objet d’une
résolution dynamique des liens en la déclarant comme virtuelle (mot clé
virtual).
Cette déclaration doit se faire dans la classe la plus générale qui admet cette
méthode (c’est-à-dire lors du prototypage d’origine).
Les redéfinitions éventuelles dans les sous-classes seront aussi considérées
comme virtuelles par transitivité.
Syntaxe
virtual Type nom_fonction ( l i st e_ a rg u me nt s ) [ const ];
Exemple
class A
{
virtual void afficher () const
{
cout << " A " << endl ;
}
};
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 7/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
Exemple : Résolution statique des liens - sans méthodes virtuelles
# include < iostream >
using namespace std ;
class Forme
{
public :
Forme () { cout << " constructeur Forme <| - " ; }
void dessiner () { cout << " je dessine ... une forme ?\ n " ; }
};
class Cercle : public Forme
{
public :
Cercle () { cout << " Cercle \ n " ; }
void dessiner () { cout << " je dessine un Cercle !\ n " ; }
};
class Triangle : public Forme
{
public :
Triangle () { cout << " Triangle \ n " ; }
void dessiner () { cout << " je dessine un Triangle !\ n " ; }
};
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 8/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
Exemple : Résolution statique des liens - sans méthodes virtuelles (Cont...)
void fa i r e Q ue lqu eCh ose ( Forme & f )
{
f . dessiner () ; // dessine une Forme
}
main ()
{
Cercle c ;
Triangle t ;
fa i r e Q u e lqu eC hos e ( c ) ; // avec un cercle
fa i r e Q u e lqu eC hos e ( t ) ; // avec un triangle
}
L’exécution du programme d’essai nous montre que nous n’obtenons pas une
liaison dynamique puisque c’est la méthode dessiner() de la classe Forme qui
est appelée :
Exécution
constructeur Forme <| - Cercle
constructeur Forme <| - Triangle
je dessine ... une forme ?
je dessine ... une forme ?
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 9/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
Exemple : Résolution dynamique des liens - méthodes virtuelles
# include < iostream >
using namespace std ;
class Forme
{
public :
Forme () { cout << " constructeur Forme <| - " ; }
// la m é thode dessiner sera virtuelle et fournira une liaison dynamique
virtual void dessiner () { cout << " je dessine ... une forme ?\ n " ; }
};
class Cercle : public Forme
{
public :
Cercle () { cout << " Cercle \ n " ; }
void dessiner () { cout << " je dessine un Cercle !\ n " ; }
};
class Triangle : public Forme
{
public :
Triangle () { cout << " Triangle \ n " ; }
void dessiner () { cout << " je dessine un Triangle !\ n " ; }
};
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 10/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
Exemple : Résolution dynamique des liens - méthodes virtuelles (Cont...)
void fa i r e Q ue lqu eCh ose ( Forme & f )
{
f . dessiner () ; // dessine une Forme
}
main ()
{
Cercle c ;
Triangle t ;
fa i r e Q u e lqu eC hos e ( c ) ; // avec un cercle
fa i r e Q u e lqu eC hos e ( t ) ; // avec un triangle
}
L’exécution du programme d’essai nous montre maintenant que nous obtenons
une liaison dynamique puisque c’est la "bonne"méthode dessiner() qui est
appelée :
Exécution
constructeur Forme <| - Cercle
constructeur Forme <| - Triangle
je dessine un Cercle !
je dessine un Triangle !
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 11/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles
En résumé : Lorsqu’une méthode virtuelle est invoquée à partir d’une
référence ou d’un pointeur vers une instance, c’est la méthode associée au
type réel de l’instance qui sera exécutée.
Attention !
I Un constructeur ne peut pas être virtuel.
I on ne peut pas invoquer de méthode virtuelle dans un constructeur (plus
exactement : l’aspect polymorphique est ignoré dans le constructeur, seule
l’instance correspondant à la classe courante est appelée).
I il est conseillé de toujours définir les destructeurs comme virtuels.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 12/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles pures
Au sommet d’une hiérarchie de classe, il n’est pas toujours possible de :
I donner une définition générale de certaines méthodes, compatibles avec toutes
les sous-classes,
I ...même si l’on sait que toutes ces sous-classes vont effectivement
implémenter ces méthodes
Une méthode virtuelle pure, ou abstraite, est incomplètement spécifiée :
I elle n’a pas de définition dans la classe où elle est introduite (pas de corps)
I elle sert à signaler aux sous-classes qu’elles doivent redéfinir la méthode
virtuelle héritée
Syntaxe
virtual Type nom_fonction ( l i st e_ a rg u me nt s ) [ const ] = 0 ;
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 13/24
Polymorphisme
Gestion des exceptions
Méthodes virtuelles pures
Exemple : Méthodes virtuelles pures
# include < iostream >
using namespace std ;
class Forme
{
public :
Forme () { cout << " constructeur Forme <| - " ; }
// la m é thode dessiner sera virtuelle pure
virtual void dessiner () = 0;
};
class Cercle : public Forme
{
public :
Cercle () { cout << " Cercle \ n " ; }
void dessiner () { cout << " je dessine un Cercle !\ n " ; }
};
class Triangle : public Forme
{
public :
Triangle () { cout << " Triangle \ n " ; }
void dessiner () { cout << " je dessine un Triangle !\ n " ; }
};
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 14/24
Polymorphisme
Gestion des exceptions
Classes abstraites
Une classe abstraite est une classe contenant au moins une méthode virtuelle
pure.
Elle ne peut être instanciée.
Ses sous-classes restent abstraites tant qu’elles ne fournissent pas les
définitions de toutes les méthodes virtuelles pures dont elles héritent
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 15/24
Polymorphisme
Gestion des exceptions
Gestion des exceptions
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 16/24
Polymorphisme
Gestion des exceptions
Gestion des exceptions
Lors de l’écriture d’un programme, la prise en compte d’erreurs prend une
place très importante si l’on souhaite écrire un programme robuste.
Par exemple, la simple ouverture d’un fichier peut provoquer beaucoup
d’erreurs telles que l’inexistence du fichier, un mauvais format, une
interdiction d’accès, une erreur de connexion au périphérique, ...
Pour que le programme soit robuste, il faut que toutes les erreurs
possibles soient détectées et traitées.
Certains langages de programmation, dont le langage C++, proposent un
mécanisme de prise en compte des erreurs, fondé sur la notion d’exception.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 17/24
Polymorphisme
Gestion des exceptions
Gestion des exceptions
Les méthodes traditionnelles de gestion d’erreurs d’exécution consistent à:
I Traiter localement l’erreur : la fonction doit prévoir tous les cas possibles qui
peuvent provoquer l’erreur et les traiter localement.
I Retourner un code d’erreur : la fonction qui rencontre une erreur retourne un
code et laisse la gestion de l’erreur à la fonction appelante.
I Arrêter l’exécution du programme (abort,...)
C++ introduit la notion d’exception:
I Une exception est l’interruption de l’exécution d’un programme à la suite d’un
événement particulier.
I Une fonction qui rencontre un problème lance une exception, l’exécution du
programme s’arrête et le contrôle est passé à un gestionnaire (handler)
d’exceptions, qui traitera cette erreur, on dit qu’il intercepte l’exception.
I Ainsi, le code d’une fonction s’écrit normalement sans tenir compte des cas
particuliers puisque ces derniers seront traités par le gestionnaire des
exceptions.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 18/24
Polymorphisme
Gestion des exceptions
Gestion des exceptions
La notion d’exception repose donc sur l’indépendance de la détection de
l’erreur et de son traitement.
Elle permet de séparer le code de gestion de l’erreur et du code où se
produit l’erreur ce qui donne une lisibilité et une modularité de traitement
d’erreurs.
Découpage du traitement d’erreur en deux parties :
I le déclenchement : instruction throw
throw e ; // Si le type de e est X , on dit que l ’ exception
// lev é e est de type X .
I le traitement : deux instructions inséparables try et catch
try {
// Un code qui peut lever une ou plusieurs exceptions
// Les fonctions appel é es ici peuvent aussi lever une exception
}
catch ( X & e ) {
// Code de gestion des exceptions lev é e par un param è tre de type X
// l ’ argument e est facultatif , il repr é sente le param è tre avec
// lequel l ’ exception a é t é lev é e
}
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 19/24
Polymorphisme
Gestion des exceptions
Gestion des exceptions
Une exception est levée ...
Si l’instruction en faute n’est pas dans un bloc try, il y appel immédiat de la
fonction terminate.
Si l’instruction en faute est incluse dans un bloc try, le programme saute
directement vers les gestionnaires d’exception catch qu’il examine
séquentiellement dans l’ordre du code source :
I Si l’un des gestionnaires catch correspond au type de l’exception, il est
exécuté, et, s’il ne provoque pas lui même d’interruption ou ne met fin à
l’exécution du programme, l’exécution se poursuit à la première ligne de code
suivant l’ensemble des gestionnaires d’interruption. En aucun cas il n’est
possible de poursuivre l’exécution à la suite de la ligne de code fautive.
I Si aucun gestionnaire ne correspond au type de l’exception, celle-ci est
propagée au niveau supérieur de traitement des exceptions (cas de blocs try
imbriqués) jusqu’à arriver au programme principal qui lui appellera terminate.
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 20/24
Polymorphisme
Gestion des exceptions
Relancer une exception
Exemple
# include < stdexcept > // pour std :: range_error
double inverse ( double x )
{
if ( x == 0.0) // lance une exception
throw range_error ( " Division par zero !\ n " ) ;
else return 1/ x ;
}
try
{
inverse (0.0) ;
}
catch ( range_error & e )
{
// traitement local
throw ; // relance l ’ exception
}
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 21/24
Polymorphisme
Gestion des exceptions
Lancer une exception
Exemple
# include < stdexcept >
# include < iostream >
float division ( int a , int b )
{
if ( b == 0) throw 1;
else return static_cast < float >( a ) / b ;
}
void test ()
{
int a , b ; cin >> a ; cin >> b ;
cout << division (a , b ) << endl ;
cout << " Calcul fini " << endl ;
}
int main ()
{
try {
test () ;
cout << " Test ex é cut é " << endl ;
}
catch ( int i ) {
cout << " Erreur d é tect é e " << i << endl ;
}
return 0;
}
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 22/24
Polymorphisme
Gestion des exceptions
Déclarer des exceptions
Selon la norme, les exceptions peuvent être de n’importe quel type (y
compris un simple entier). Toutefois, il est utile de les définir en tant que
classes. Pour cela, on dérive la classe fournit en standard std::exception
et on surchargera au minimum la méthode what().
Exemple
class ErreurX : public exception
{
public :
ErreurX () throw () {}
~ ErreurX () throw () {}
const char * what ( void ) const throw ()
{
// on peut aussi utiliser un string en priv é
return " Exception sur ... " ;
}
};
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 23/24
Polymorphisme
Gestion des exceptions
Déclarer des exceptions
Exemple (Cont...)
try
{
// bloc de code prot é g é
}
catch ( ErreurX & e )
{
cerr << " Erreur : " << e . what () << endl ;
}
catch ( exception & e )
{
cerr << " Exception inconnue : " << e . what () << endl ;
cerr << " Fin du programme " << endl ; // ou pas
exit (1) ;
}
Pr. Abdessamad EL BOUSHAKI (FST Marrakech) Programmation Orientée Objet en C++ 24/24