Modèles
Modèles
Pour illustrer simplement les modèles (mot clé template), imaginons que vous ayez besoin de développer un programme de gestion du temps. Pour afficher le calendrier, vous pourriez utiliser un tableau d’objets Date, par exemple, et pour gérer le planning quotidien un tableau d’objets Heure. Les tableaux sont très souvent utilisés en programmation et vous pouvez y stocker aussi bien des types intégrés que des types définis par l’utilisateur. En créant un modèle de classe, vous allez pouvoir définir une seule fois une classe tableau d’objets génériques et l’utiliser chaque fois que vous aurez besoin de stocker des éléments sous forme de tableau en précisant le type d’objet qui y sera stocké. De la même façon, en créant un modèle de fonction d’affichage, vous pourrez utiliser cette dernière pour afficher indifféremment un objet de type date ou un objet de type heure.Définition et implémentation d’un modèle de classe
Dans cet exemple, nous utilisons une classe tableau qui, en comparaison des tableaux prédéfinis, présente un avantage important : sa taille peut s’adapter de façon dynamique au nombre et à la taille des éléments, vous ne risquez donc pas de générer des erreurs de dépassement de capacité (en clair, écraser la mémoire au-delà de la zone initialement allouée au tableau).Le code 5.1 (fichier en-tête 05-01.h) contient la déclaration d’une classe simple Date et du modèle de classe Tableau. Dans le code 5.2 (fichier source 05-01.cpp), nous définissons la classe et le modèle que nous utilisons ensuite pour créer un tableau d’objets Date.
Code 5.1 : fichier en-tête 05-01.h
#include<iostream> //Pour les entrées/sorties using namespace std; class Date //déclaration de la classe Date { private: int jour, mois, annee; public: Date(); //constructeur par défaut Date(int,int,int); //Constructeur ~Date(); //Destructeur /*** Méthodes d’accès ***/ void DefinirDate(int j,int m,int a) { jour=j; mois=m; annee=a; } void LireDate() const { cout<<jour<<" "<<mois<<" "<<annee<<endl; } int LireJour() const { return jour; } int LireMois() const { return mois; } int LireAnnee() const { return annee; } //opérateur = surchargé pour Date Date operator=(const Date); }; //déclaration du modèle avec son type paramètre (T) : template <class T> class Tableau //déclaration de la classe "paramètre" { private: T * p_tab; //déclaration du pointeur de tableau p_tab //autre élément important d’un tableau, sa taille : int taille; public: //Constructeur Tableau(int taille); Tableau(const Tableau &source); //Constructeur de copie ~Tableau() { delete [] p_tab; } //Destructeur //surcharge de l’opérateur = : Tableau& operator=(const Tableau&); //surcharges de l’opérateur [] : T& operator[] (int index) { return p_tab[index]; } const T& operator[](int index) const { return p_tab[index]; } //méthode d’accès int LireTaille() const { return taille; } };
Code 5.2 : fichier source 05-01.cppxe "modèle:de classe tableau"
1 : #include"05-01.h" //déclarations Date et modèle 2 : 3 : int taille_defaut=4; 4 : 5 : /***Constructeurs et destructeur de Date***/ 6 : Date::Date() 7 : { jour=0; mois=0; annee=0; } 8 : Date::Date(int j,int m,int a) 9 : { jour=j; mois=m; annee=a; } 10: 11: Date::~Date() { } 12: 13: /****Définition de l’opérateur = surchargé pour Date****/ 14: 15: Date Date::operator=(const Date source) 16: { 17: if (this == &source) //si les deux objets sont égaux 18: return *this; //on renvoie tout de suite l’objet 19: jour = source.LireJour(); //sinon on copie jour 20: mois = source.LireMois(); //puis mois 21: annee = source.LireAnnee(); //et enfin annee 22: return source; 23: } 24:/*Définition du constructeur du modèle de 25: classe Tableau*/ 26: template <class T> 27: //initialisation de la donnée membre taille : 28: Tableau<T>::Tableau(int t=taille_defaut):taille(t) 29: { 30: p_tab = new T[t]; 31: } 32: 33: /****Définition du constructeur de copie du modèle****/ 34: 35: template <class T> 36: Tableau<T>::Tableau(const Tableau &source) 37: /*le constructeur de copie reçoit en argument l’adresse 38: d’un tableau source protégé par const*/ 39: { 40: /*on détermine la taille en fonction de celle du tableau 41: source :*/ 42: taille = source.LireTaille(); 43: p_tab = new T[taille]; //on réserve la mémoire 44: // appropriée dans le tas 45: for(int i=0; i<taille; i++) //on recopie les éléments 46: p_tab[i]=source[i]; 47: } 48: 49: /****Définition de l’opérateur = pour le modèle****/ 50: template <class T> 51: Tableau<T>& Tableau<T>::operator=(const Tableau &source) 52: { 53: if (this == &source) //si les 2 tableaux sont égaux 54: return *this; //on renvoie tout de suite l’objet cible 55: // et la suite de l’instruction if n’est pas exécutée 56: delete [] p_tab; //on supprime l’objet cible 57: /*on détermine sa taille en fonction de celle du tableau 58: source :*/ 59: taille = source.LireTaille(); // 60: /*on réserve de la mémoire dans le tas pour "recréer" 61: l’objet cible : */ 62: p_tab = new T[taille]; 63: /*on recopie chaque élément de l’objet source dans 64: l’objet cible : */ 65: for (int i=0; i<taille; i++) 66: p_tab[i] = source[i]; 67: return *this; //on renvoie l’objet cible 68: } 69: 70: int main() 71: { 72: Tableau<Date> Tab_date; //on crée un tableau de dates 73: Date *p_date; //on déclare un pointeur d’objet Date 74: /** Les tableaux peuvent être volumineux, c’est pourquoi 75: il est préférable de les créer dans le tas et de les 76: manipuler via des pointeurs **/ 77: 78:cout<<"Saisissez les dates importantes à enregistrer (ex 5 12 2009): "<<endl; 79: //pour chaque élément du tableau : 80: for (int i=0; i<Tab_date.LireTaille(); i++) 81: { 82: int j, m, a; 83: cout << "ttDate "<<"i" <<": "; 84: cin >> j; //on récupère la valeur 85: cin >> m; //saisie par l’utilisateur 86: cin >> a; 87: p_date = new Date(j, m, a); //on réserve la mémoire 88: //pour le nouvel élément 89: Tab_date[i] = *p_date; //on stocke sa valeur à 90: // l’emplacement réservé 91: } 92: 93: cout << "Voici le tableau des dates enregistrées: n" ; 94: for (int k=0; k<Tab_date.LireTaille(); k++) 95: { 96: Tab_date[k].LireDate() ; 97: } 98: }
L’exécution de ce programme donne le résultat suivant :
Saisissez les dates importantes à enregistrer (ex 5 12 2009): Date 1 : 1 1 2010 Date 2 : 2 2 2010 Date 3 : 3 3 2010 Date 4 : 4 4 2010 Voici le tableau des dates enregistrées: 1 1 2010 2 2 2010 3 3 2010 4 4 2010
À savoir
En C++, le nom donné à unxe "pointeur:de tableau" tableau est un pointeur constant sur le premier élément de ce dernier. Cela signifie que tab_date est équivalent à &tab_date[0]. La numérotation des éléments s’effectue comme en C : en partant de zéro.Quelques remarques sont nécessaires à propos de la syntaxe de ce programme.
- Ligne 28 : jusqu’à présent, nous avions initialisé les données membres des objets en leur affectant une valeur dans le corps du constructeur. Vous pouvez aussi le faire sur la première ligne de définition de ce constructeur en appliquant la syntaxe classe::classe():var1(valeur1),var2(valeur2),etc. {}.
- Remarquez les nombreuses similitudes entre le constructeur de copie du modèle et la définition de l’opérateur = surchargé. Les raisons sont évidentes.
- Ligne 52 : this est le pointeur sur les données de l’objet, *this représente donc l’objet lui-même .
Définition et implémentation d’un modèle de fonction
Un modèle de fonction est une fonction qui peut être utilisée avec divers types d’objets. Pour illustrer ce concept, nous allons ajouter à notre exemple précédent une classe Heure et une fonction modèle Affiche() qui nous permettra d’afficher l’un ou l’autre type d’objet.Code 5.3 : déclaration et définition des classes Date et Heure (fichier 05-03.h)
#include<iostream> //Pour les entrées/sorties using namespace std; class Heure; //Déclaration de la classe Heure class Date //déclaration et définition de la classe Date { private: int jour, mois, annee; public: Date(); //constructeur par défaut Date(int,int,int); //Constructeur ~Date(); //Destructeur //méthodes d’accès void DefinirDate(int j,int m,int a) { jour=j; mois=m; annee=a; } int LireJour() const { return jour; } int LireMois() const { return mois; } int LireAnnee() const { return annee; } //opérateur = surchargé pour Date Date operator=(const Date); //opérateur ami (voir section suivante): friend ostream& operator<< (const Date&, const Heure&); }; class Heure //définition de la nouvelle classe Heure { private: int heure, minute; public: Heure(int,int); //Constructeur ~Heure(); //Destructeur //méthodes d’accès void DefinirHeure(int h,int m){ heure=h; minute=m; } int LireHeure() const { return heure; } int LireMinute() const { return minute; } //opérateur ami (voir section suivante): friend ostream& operator<< (const Date&, const Heure&); };
Code 5.4 : utilisation d’un modèle de fonction (fichier 05-04.cpp)
#include"05-03.h" //Inclusion des déclarations Date et Heure /****Constructeurs et destructeur de Date****/ Date::Date() { jour=0; mois=0; annee=0; } Date::Date(int j,int m,int a) { jour=j; mois=m; annee=a; } Date::~Date() { } /****Constructeur et destructeur de Heure****/ Heure::Heure(int h,int m) { heure=h; minute=m; } Heure::~Heure() { } /****opérateur << surchargé pour afficher une date ****/ ostream& operator<< (ostream& sortie, const Date une_date) { sortie << une_date.LireJour() << " " << une_date.LireMois() << " " << une_date.LireAnnee(); return sortie; } /****opérateur << surchargé pour afficher une heure ****/ ostream& operator<< (ostream& sortie, const Heure une_heure) { sortie << une_heure.LireHeure() << "h" << une_heure.LireMinute(); sortie << "mn"; return sortie; } //déclaration du modèle avec son type paramètre (T) : template <class T> //déclaration de la fonction "paramétrée" : void Affiche(T x) { cout << x << endl; //on affiche l’objet à condition que } //ce dernier implémente l’opérateur surchargé << int main() { cout << "nNous créons l’objet Date_initiale = 1 1 2009 "; Date Date_initiale(1,1,2009); cout << "nPuis l’objet Heure_initiale = 12h30 " << endl; Heure Heure_initiale(12,30); cout << "nNous affichons maintenant ces deux objets avec Affiche()n" << endl; cout << "Date:t"; Affiche(Date_initiale); cout << "Heure:t"; Affiche(Heure_initiale); }
L’exécution de ce programme produit l’affichage suivant :
Nous créons l’objet Date_initiale = 1 1 2009 Puis l’objet Heure_initiale = 12h30 Nous affichons maintenant ces deux objets avec Affiche() Date: 1 1 2009 Heure: 12h30mn
Vous pouvez constater que la syntaxe de déclaration d’une fonction modèle est assez simple. N’oubliez pas, cependant, que tout opérateur apparaissant dans le corps de cette fonction doit être implémenté pour tous les types d’objet manipulés (comme l’opérateur << dans notre exemple).
Le texte original de cette fiche pratique est extrait de
«Tout sur le C++» (Christine EBERHARDT, Collection
CommentCaMarche.net, Dunod, 2009)