Classe abstraite et fonction virtuelle pure

Décembre 2016

Classe abstraite et fonction virtuelle pure

Dans l’exemple de code 7.3(fonctions virtuelles), la classe Noeud a ceci de particulier qu’elle représente uniquement un concept. Elle a été définie pour isoler les caractéristiques générales des différents types de nœuds (ListeChainee, NoeudInterne, et Queue) et le programme ne créera pas d’objets de ce type, seulement des objets basés sur ce type. On dit qu’il s’agit d’une classe abstraite ou d’un type de donnée abstrait (vous pourrez entendre parler d’ADT pour Abstract Data Typexe "ADT (Abstract Data Type)").

En C++, vous créez une classe abstraite en déclarant au moins une fonction virtuelle pure, c’est-à-dire en initialisant une fonction virtuelle à zéro (voir Afficher() et Insérer() dans le code 7.4). L’idée est d’imposer ainsi aux concepteurs des classes dérivées de redéfinir et de fournir une implémentation concrète de la fonction.
Voici le code de cette classe :

Code 7.4 : la classe abstraite Noeud

Class Noeud  
{  
private:  

public:  
   Noeud(){}  
   virtual ~Noeud(){}  
//les 2 fonctions virtuelles pures  
   virtual Noeud * Inserer(Donnees *un_objet) = 0;   
   virtual void Afficher() = 0;  
};


Pour améliorer la liste chaînée créée au code 7.3, nous pourrions transformer la classe Donnees en type abstrait pour offrir à l’utilisateur la possibilité de stocker différents types de données dans la liste chaînée et pour traiter ces différents types de Donnees de façon polymorphique. En version décodée, la liste chaînée va simplement manipuler des types Donnees sans avoir besoin de connaître les caractéristiques de chaque type qui en sera dérivé.

Le code 7.5 présente uniquement la classe Donnees modifiée, les deux nouvelles classes qui en sont dérivées et la nouvelle fonction main(). Pour compléter ce programme, ajoutez les déclarations et définitions des classes Noeud, ListeChainee, NoeudInterne, et Queue.

Code 7.5 : une liste chaînée plus « polymorphique »xe "liste chaînée:polymorphique"

/*******************Classe Donnees**********************/  
class Donnees                   //classe abstraite  
{  
public:  
   Donnees(int val):valeur_objet(val){}  
   virtual ~Donnees(){}  
   int Comparer(const Donnees &);  
   virtual void Afficher() = 0;   //fonction virtuelle pure  
protected:  
   int valeur_objet;  
};  

/****Définition de la fonction Comparer de Donnees******/  
int Donnees::Comparer(const Donnees & autre_objet)  
{  
   if (valeur_objet < autre_objet.valeur_objet)  
      return plus_petit;  
   if (valeur_objet > autre_objet.valeur_objet)  
      return plus_grand;  
   else  
      return egal;  
}  

/****************Classe DonneeEntiere******************/  
class DonneeEntiere : public Donnees  
{  
public:  
   //le constructeur initialise val :  
   DonneeEntiere(int val) : Donnees(val) {}  
   virtual ~DonneeEntiere() {}  
   virtual void Afficher();  
private:  
};  

/*Implémentation de la fonction virtuelle pure Afficher*/  
void DonneeEntiere::Afficher()  
{  
   cout << "La valeur de l’entier est " << valeur_objet << endl;  
}  

/******************Classe DonneeGraphique*****************/  
class DonneeGraphique : public Donnees  
{  
public:  
   DonneeGraphique(int val) : Donnees(val) {}  
   virtual ~DonneeGraphique() {}  
   virtual void Afficher();  
private:  
};  

/*Implémentation de la fonction virtuelle pure Afficher*/  
void DonneeGraphique::Afficher()  
{  
   cout << "(" << valeur_objet << " étoiles): ";;  
   for ( int i = 0; i < valeur_objet; i++)  
      cout << "*";  
   cout << endl;  
}  

/****************Fonction principale****************/  
int main()  
{  
   Donnees * p_donnees;   //on crée un objet Donnees de  
                          // pointeur p_donnees  
   int val;               //valeur à stocker dans la liste  
   int type_donnees;      //type de la valeur val  
   ListeChainee lc;       //on crée un objet listechainee  

   for (;;)      //tant que l’utilisateur de saisi pas 0  
   {  
     cout << "[1] Entier, [2] Etoiles, [0] Quitter: ";  
     cin >> type_donnees; //on lit la valeur saisie  
     if (!type_donnees)   //si 0  
     break;               //on termine  
     switch(type_donnees)  
     {  
     case 1:               //si 1  
 cout << "Quelle valeur voulez-vous ajouter à la liste ? ";  
     cin >> val;         //on lit la valeur entière  
     p_donnees = new DonneeEntiere(val);  
     break;  
     case 2:              //si 2  
     cout << "Quelle quantité d’étoiles voulez-vous ajouter à la liste ? ";  
     cin >> val;  //on lit toujours une valeur entière mais  
                  //elle représente cette fois une quantité  
     p_donnees = new DonneeGraphique(val);  
     }  

      lc.Inserer(p_donnees); //on stocke dans la liste  
   }  
   cout << "\n\n";  
   lc.Afficher();   //on affiche les valeurs stockées dans   
   cout << "\n\n";  //la liste par ordre croissant  
   return 0;  
}


En exécutant ce programme, on obtient le résultat suivant :
[1] Entier, [2] Etoiles, [0] Quitter: 1  
Quelle valeur voulez-vous ajouter à la liste ? 5  
[1] Entier, [2] Etoiles, [0] Quitter: 2  
Quelle quantité d’étoiles voulez-vous ajouter à la liste ? 10  
[1] Entier, [2] Etoiles, [0] Quitter: 1  
Quelle valeur voulez-vous ajouter à la liste ? 3  
[1] Entier, [2] Etoiles, [0] Quitter: 2  
Quelle quantité d’étoiles voulez-vous ajouter à la liste ? 2  
[1] Entier, [2] Etoiles, [0] Quitter: 0  

<2 étoiles>: **  
La valeur de l’entier est 3  
La valeur de l’entier est 5  
<10 étoiles>: **********

Substitution d’une fonction virtuelle pure

Les classes concrètes doivent absolument définir toutes les fonctions virtuelles pures, mais la classe abstraite a la possibilité d’en définir une. Notre classe Donnees pourrait fournir l’implémentation de la fonction Afficher(), par exemple :

Code 7.6 : implémentation d’une fonction virtuelle pure dans la classe de base

1 :class Donnees  
2 :{  
3 :public:  
4 :   Donnees(int val):valeur_objet(val){}  
5 :   virtual ~Donnees(){}   //destructeur virtuel  
6 :   int Comparer(const Donnees &);  
7 :   virtual void Afficher() = 0; //fonction virtuelle pure  
8 :protected:  
9 :   int valeur_objet;  
10:};  
11://implémentation d’un affichage de base :  
12:void Donnees::Afficher()  
13:{  
14:   cout << “\nVoici la valeur de vos données: \n”;  
15:}


Les classes dérivées appellent cette fonction via l’opérateur de résolution de portée ::. Cela permet à la classe abstraite d’offrir une fonction de base tout en imposant aux fonctions dérivées l’implémentation d’une fonction plus spécialisée :

void DonneeGraphique::Afficher()  
{  
  Donnees::Afficher(); // appelée via l’opérateur de portée  
  cout << “(“ << valeur_objet << “): “;  
  for ( int i = 0; i < valeur_objet; i++)  
     cout << “*”;  
  cout << endl;  
}


Le texte original de cette fiche pratique est extrait de
«Tout sur le C++» (Christine EBERHARDT, Collection
CommentCaMarche.net, Dunod, 2009)

A voir également :

Ce document intitulé «  Classe abstraite et fonction virtuelle pure  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.