Reprenons l'exemple des classes point et emplacement avec une nouvelle classe cercle qui hérite de point. Ces classes sont manipulés à l'aide de pointeurs.
... class cercle : public point { int rayon ; public: cercle(const int rr=20,const int coul=1, const int xx=0, const int yy=0) ; void affiche(void) const ; void changeRayon(const int rr) ; } ; inline cercle::cercle(const int rr, const int coul, const int xx, const int yy) : rayon(r),point(coul,xx,yy) {} void cercle::affiche(void) { cout << "cercle" << endl ; } inline void cercle::changeRayon(const int rr) { rayon=rr ; } int main(void) { point *ptr_point ; cercle * ptr_cercle ; ptr_point = new point (3,5,5) ; ptr_cercle = new cercle (50,3,100,100) ; ptr_point->affiche() ; // affiche un point ptr_cercle->affiche() ; // affiche un cercle ptr_cercle->point::affiche() ; // affiche un point // (pas de sens) ptr_cercle->changeCouleur(5) ; // ok ptr_point->changeRayon(10) ; // erreur delete ptr_point ; delete ptr_cercle ; ptr_cercle = new point ; // erreur de compilation ptr_point = new cercle ; // ok ptr_point->changeCouleur(2) ; //ok ptr_point->affiche() ; // affiche un point ptr_point->changeRayon(30) ; //erreur ...Une variable pointeur d'une sur-classe peut pointer sur une sous-classe (l'inverse est faux). Dans ce cas, lors d'un appel à un membre défini dans les deux classes, le compilateur repose sur un mécanisme de résolution statique : c'est le type de la variable pointeur donné à déclaration de la variable qui permet de sélectionner le membre (celui de la super-classe). Dans notre cas, il serait interressant d'avoir un mécanisme de résolution dynamique, ce qui permettrait d'executer le membre de la classe qui a été alloué. Ce mécanisme est possible en C++ par l'introduction du mot-clé virtual.
class point { ... virtual void affiche(void) const ; } ; ... int main(void) { point *ptr_point = new cercle(5,5,100,100) ; ptr_point->affiche() ; // affiche un cercle ...le passage de paramètre par valeur impose le mécanisme de résolution statique.
void fonction(point p) { p.affiche() ; } int main(void) { point p ; cercle c ; fonction(p) ; // affiche un point fonction(c) ; // affiche un pointL'utilisation de réference au passage de paramètre résoud le problème (il faut bien sur que point::affiche soit déclarée virtuelle). Le mécanisme de résolution tardive ou dynamique est largement utilisée dans la conception de programmes orienté objet. Une nouvelle notion appara^t : celle de classe abstraite. C'est une classe dont tous les membres sont virtuels et non définis. Cette classe n'a pas de sens dans la vie réelle , seules les classes dérivés de celle-ci en ont une, comme dans l'exemple ci-après :
class figure : public emplacement { int couleur ; public: figure(const int coul=1,const int xx=0,const int yy=0) ; virtual void affiche(void) const = 0 ; void changeCouleur(const int c) ; } ; class point : public figure { public: point(const int coul=1,const int xx=5, const int yy=5) ; void affiche(void) const ; } ; class cercle : public figure { ... void affiche(void) const ; } ; ... int main(void) { figure *pfig ; int reponse ; cout << "voulez-vous un cercle (1) ou un point (2) ; cin >> reponse ; if (reponse==1) pfig = new cercle ; else pfig = new point ; pfig->affiche() ; // polymorphisme return 0 ; }La classe figure est une classe abstraite car elle contient des fonctions virtuelles non définies (symbole '=0'). Ce n'est pas une classe abstraite pure puisqu'elle contient des membres définis (gestion de la couleur). Cependant, il ne peut jamais y avoir de variable correspondant à une classe abstraite, car un appel à une fonction non définie déclencherait une erreur.