Classes génériques

Il n’y a pas, dans les premières versions de C++, de moyen de définir une classe générique, c’est-à-dire dépendant d’un paramètre comme le type d’un élément contenu dans la classe. On peut toutefois le simuler en utilisant une macro. Dans les versions plus récentes, cette fonctionnalité a été ajoutée sous le nom anglais de template (en français, gabarits).

Revenons à notre exemple de la liste chaînée (chapitre 6). Cette liste utilise une classe element qui peut désigner n’importe quoi ; c’est caractéristique d’une classe générique.

Pour changer facilement le type d’élément de liste, il y a deux possibilités. La première consiste à créer un fichier séparé liste.h ne contenant pas la définition de element, par exemple comme ceci (en simplifiant beaucoup la définition de la classe noeud) :

liste.h

class noeud {     noeud *suivt;     element elm;     public :     noeud(element e, noeud *suivant = 0)         { elm = e; suivt = suivant; }     noeud *suivant(void) { return suivt; }     element &contenu(void) { return elm; }     };

 

Dans ce cas, avant d’inclure liste.h dans votre fichier, il faudra écrire la définition de element :

typedef double element;#include "liste.h"

par exemple. C’est la méthode que nous avons employée jusqu’à présent. Elle a toutefois l’inconvénient de ne permettre l’utilisation que d’un seul type de liste chaînée.

Une seconde méthode consiste à écrire les deux macros suivantes (dans un fichier séparé en général, que nous nommons encore liste.h) :

liste.h

#include <generic.h>				#define noeud(typ) _Paste2(noeud_, typ)				#define listedeclare(typ)    \class noeud(typ) {    \     noeud(typ) *suivt;    \     typ elm;        \    public :    \     noeud(typ)(typ e, noeud(typ) *suivant = 0)    \         { elm = e; suivt = suivant; }    \     noeud(typ) *suivant(void) { return suivt; }    \     typ &contenu(void) { return elm; }    \     }

 

Le fichier <generic.h> contient un certain nombre de macros pour coller des éléments, dont voici les principales :

#define _Paste2(x, y) x##y// coller x et y ensemble#define declare(x, y) _Paste2(x, declare)(y)// déclarer l’objet x avec paramètre y#define implement(x, y) _Paste2(x, implement)(y)// définir l’objet x avec paramètre y

À présent, écrivons le programme suivant :

#include "liste.h"declare(liste, int);				declare(liste, double);		main()    {        noeud(int) *ni = new noeud(int)(0);        noeud(double) *nd = new noeud(double)(3.14);        // ...    }

Ce programme sera transformé ainsi par le préprocesseur :

class noeud_int {        noeud_int *suivt;        int elm;    public :        noeud_int(int e, noeud_int *suivant = 0)                 { elm = e; suivt = suivant; }        noeud_int *suivant(void) { return suivt; }        int &contenu(void) { return elm; }        };			class noeud_double {        noeud_double *suivt;        double elm;    public :        noeud_double(double e, noeud_double *suivant = 0)                    { elm = e; suivt = suivant; }        noeud_double *suivant(void) { return suivt; }        double &contenu(void) { return elm; }        };			main()    {        noeud_int *ni = new noeud_int(0);        noeud_double *nd = new noeud_double(3.14);        // ...    }

Avec nos clauses declare, on a donc en fait déclaré deux classes noeud_int et noeud_double. Les noms de ces classes peuvent être utilisés tels quels, ou sous la forme de macros noeud(int) et noeud(double) qui donne l’illusion d’une classe générique. Notons qu’il faut toutefois effectivement une déclaration pour chaque type utilisé, ce qui ne serait pas le cas avec une vraie classe générique comme il en existe dans certains langages comme ADA, ou les versions récentes de C++.

Les implémentations, lorsqu’il y en a (fonctions qui ne sont pas en ligne en particulier), seront définies dans une seconde macro listeimplement(typ) et on écrira implement(typ) dans les fichiers ayant besoin de ces implantations.

On gagne ainsi une certaine facilité d’utilisation, moyennant un surcoût au moment de l’écriture des classes génériques.

Précédent Précédent Sommaire Sommaire Suivant Suivant