Les patrons de classes
Définition d’un patron de classes
On précise les paramètres de type en les faisant précéder du mot clé class et les paramètres expression en mentionnant leur type dans une liste de paramètres introduite par le mot template (comme pour les patrons de fonctions, avec cette différence qu’ici, tous les paramètres – type ou expression – apparaissent). Par exemple : template class gene { // ici, T désigne un type quelconque, n une valeur entière quelconque } ; Si une fonction membre est définie (ce qui est le cas usuel) à l’extérieur de la définition du patron, il faut rappeler au compilateur la liste de paramètres (template) et préfixer l’en-tête de la fonction membre du nom du patron accompagné de ses paramètres. (En toute rigueur, il s’agit d’une redondance constatée, mais non justifiée, par le fondateur du langage lui-même, Stroustrup.) Par exemple, pour un constructeur de notre patron de classes précédent : template gene ::gene (…) { ….. } Instanciation d’une classe patron On déclare une classe patron en fournissant à la suite du nom de patron un nombre de paramètres effectifs (noms de types ou expressions) correspondant aux paramètres figurant dans la liste (template). Les paramètres expression doivent obligatoirement être des expressions constantes du même type que celui figurant dans la liste. Par exemple, avec notre précédent patron (on suppose que pt est une classe) : class gene c1 ; // T = int, U = float, n = 5 class gene c2 ; // T = int, U = int, n = 12 const int NV=100 ; class gene c3 ; // T = pt, U = double, n=100 int n = 5 ; class gene c4 ; // erreur : n n’est pas constant const char C = ‘e’ ; class gene c5 ; // erreur : C de type char et non int Un paramètre de type effectif peut lui-même être une classe patron. Par exemple, si nous avons défini un patron de classes point par : template class point { ….. } ; Voici des instances possibles de gene : class gene , float, 10> c5 ; // T=point, U=float, n=10 class gene , point, 5> c6 ; // T=point, U=point, n=5 Un patron de classes peut comporter des membres (données ou fonctions) statiques ; dans ce cas, chaque instance de la classe dispose de son propre jeu de membres statiques. Les patrons de classes Spécialisation d’un patron de classes Un patron de classes ne peut pas être surdéfini (on ne peut pas définir deux patrons de même nom). En revanche, on peut spécialiser un patron de classes de différentes manières. — En spécialisant une fonction membre Par exemple, avec ce patron : template class tableau { ….. } ; On pourra écrire une version spécialisée de constructeur pour le cas où T est le type point et où n vaut 10 en procédant ainsi : tableau :: tableau (…) { ….. } — En spécialisant une classe Dans ce cas, on peut éventuellement spécialiser tout ou une partie des fonctions membre, mais ce n’est pas nécessaire. Par exemple, avec ce patron : template class point { ….. } ; on peut fournir une version spécialisée pour le cas où T est le type char en procédant ainsi : class point { // nouvelle définition de la classe point pour les caractères } ; Identité de classes patron On ne peut affecter entre eux que deux objets de même type. Dans le cas d’objets d’un type classe patron, on considère qu’il y a identité de type lorsque leurs paramètres de types sont identiques et que les paramètres expression ont les mêmes valeurs. Classes patron et héritage On peut « combiner » de plusieurs façons l’héritage avec la notion de patron de classes : ■ Classe « ordinaire » dérivée d’une classe patron ; par exemple, si A est une classe patron définie par template A : class B : public A // B dérive de la classe patron A On obtient une seule classe nommée B. ■ Patron de classes dérivé d’une classe « ordinaire » ; par exemple, si A est une classe ordinaire : template class B : public A On obtient une famille de classes (de paramètre de type T). ■ Patron de classes dérivé d’un patron de classes ; par exemple, si A est une classe patron définie par template A, on peut : – définir une nouvelle famille de fonctions dérivées par : template class B : public A Dans ce cas, il existe autant de classes dérivées possibles que de classes de base possibles. – définir une nouvelle famille de fonctions dérivées par : template class B : public A Dans ce cas, on peut dire que chaque classe de base possible peut engendrer une famille de classes dérivées (de paramètre de type U).
Exercice 134
Énoncé On dispose du patron de classes suivant : template class point { T x, y ; // coordonnees public : point (T abs, T ord) { x = abs ; y = ord ; } void affiche () { cout << « Coordonnees : » << x << » » << y << « \n » ; } } ; On souhaite créer un patron de classes cercle permettant de manipuler des cercles, définis par leur centre (de type point) et un rayon. On n’y prévoira, comme fonctions membre, qu’un constructeur et une fonction affiche se contentant d’afficher les coordonnées du centre et la valeur du rayon. a. Le faire par héritage (un cercle est un point qui possède un rayon). b. Le faire par composition d’objets membre (un cercle possède un point et un rayon). exos_c++.book Page 273 Jeudi, 5. juillet 2007 11:10 11 Exercices en langage C++ 274 © Éditions Eyrolles a. La démarche est analogue à celle de la question a de l’exercice 132. On a affaire à un patron dépendant de deux paramètres (ici T et U) : template class cercle : public point { U r ; // rayon public : cercle (T abs, T ord, U ray) : point (abs, ord) { r = ray ; } void affiche () { point::affiche () ; cout << » rayon : » << r ; } } ; b. Le patron dépend toujours de deux paramètres (T et U) mais il n’y a plus de notion d’héritage : template class cercle { point c ; // centre U r ; // rayon public : cercle (T abs, T ord, U ray) : c(abs, ord) // pourrait r(ray) { r = ray ; } void affiche () { c.affiche () ; cout << » rayon : » << r ; } } ; Notez que, dans la définition du constructeur cercle, nous avons transmis les arguments abs et ord à un constructeur de point pour le membre c. Nous aurions pu utiliser la même notation pour r, bien que ce membre soit d’un type de base et non d’un type classe ; cela nous aurait conduit au constructeur suivant (de corps vide) : cercle (T abs, T ord, U ray) : c(abs, ord), r(ray).