0% ont trouvé ce document utile (0 vote)
9 vues23 pages

CH19-POO-C

Ce document présente une introduction aux templates en C++, permettant de créer des fonctions et classes génériques qui acceptent différents types de données. Il explique comment déclarer des types génériques, utiliser des fonctions templates pour éviter la répétition de code, et les contraintes liées à l'utilisation de ces templates. Enfin, il aborde la spécialisation des templates et la création de classes templates pour modéliser des objets avec des types variables.

Transféré par

stcoulibaly026
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
9 vues23 pages

CH19-POO-C

Ce document présente une introduction aux templates en C++, permettant de créer des fonctions et classes génériques qui acceptent différents types de données. Il explique comment déclarer des types génériques, utiliser des fonctions templates pour éviter la répétition de code, et les contraintes liées à l'utilisation de ces templates. Enfin, il aborde la spécialisation des templates et la création de classes templates pour modéliser des objets avec des types variables.

Transféré par

stcoulibaly026
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF ou lisez en ligne sur Scribd
Créez des templates o <@ eon La force des templates est d'autoriser une fonction ou une classe a utiliser des types différents. Leur marque de fabrique est la présence des chevrons < et > @ Lutilisation des templates est un sujet trés vaste. II y a des livres entiers qui y sont consacrés. Ce chapitre n'est qu'une bréve introduction au domaine. Découvrez les fonctions template ll arrive souvent qu'on ait besoin d'opérations mathématiques dans un programme. Une opération toute simple est celle qui consiste a trouver le plus grand de deux nombres. Dans le cas des nombres entiers, on pourrait écrire une fonction comme suit cpp maximum( (a>b) Une telle fonction existe dans la bibliotheque standard. Elle se trouve dans I'en-téte algorithm ,ets'appelle maximum Cette fonction est trés bien et elle n'a pas de probléme. Cependant, si un utilisateur de votre fonction souhaite utiliser des double ala place des int _, il risque d'avoir un probleme. II faudrait donc fournir également une version de cette fonction utilisant des nombres réels. Pour 6tre rigoureux, il faudrait également fournir une fonction de ce type pourles char ,les unsigned int, les nombres rationnels, etc. On se rend vite compte que la tache est trés répétitive. Cependant, il y a un point commun a toutes ces fonctions : le corps de la fonction est strictement identique. Quel que soit le type, le traitement que l'on effectue est le méme. On se rend compte que l'algorithme utilisé dans la fonction est générique. ll serait donc interessant de pouvoir écrire une seule fois la fonction en disant au compilateur : "Cette fonction est laméme pour tous les types, fais le sale boulot de recopie du code toi-méme Eh bien, cela tombe bien parce que c'est ce que permettent les templates en C++, et c'est ce que nous allons apprendre a utiliser... Le terme francais pour template est modéle. Le nom est bien choisi car il décrit précisément ce que nous allons faire. Nous allons écrire un modeéle de fonction, et le compilateur va utiliser ce modeéle dans les différents cas qui nous intéressent. Déclarez un type générique Pour indiquer au compilateur que l'on veut faire une fonction générique, on déclare un "type variable" qui peut représenter n'importe quel autre type. On parle de type générique Cela se fait de la maniére suivante cpp Vous pouvez remarquer quatre choses importantes : 1. Le mot-clé template prévient le compilateur que la prochaine chose dont on va lui parler sera générique. [Link] < et > constituent la marque de fabrique des templates. 3. Le mot-clé typename indique au compilateur que T serale nom que l'on va utiliser pour notre "type spécial" qui remplace n'importe quoi. 4. Il n'y a PAS de point-virgule a la fin de la ligne. La ligne de code précédente indique au compilateur que dans la suite, T sera un type générique pouvant représenter n'importe quel autre type. On pourra donc utiliser ce T dans notre fonction comme type pour les arguments et pour le type de retour. cpp Peso UES T maximum aa ay i if(a>b) rte se ety Quand il voit cela, le compilateur génére automatiquement une série de fonctions maximum pour tous les types dont vous avez besoin. Cela veut dire que si vous avez besoin de cette fonction pour des entiers, le compilateur crée la fonction : eesti Ga Te ae oS if(a>b) return a; os ot ..etdeméme pourles double , char ,etc C'est le compilateur qui se farcit le travail de recopie ! Parfait, on peut aller faire la sieste pendant ce temps. vere T maximum( { (a>b) int main() f double pi( Oe (ol AG maximum(pi,e) << endl; int cave(-1); rae ar 310 cout << maximum(cave,dernierEtage) Pere Tae Pra Ura YG maximum(a,b) Et tout cela se passe sans que |'on ait besoin d'écrire plus de code. II faut juste indiquer entre des chevrons quelle version de la fonction on souhaite utiliser, comme pourles vector ensomme: on devait indiquer quelle version du tableau on souhaitait utiliser. Il n'est pas toujours utile d'indiquer entre chevrons quel type on souhaite utiliser pour les fonctions templates. Le compilateur est assez intelligent pour deviner ce que vous souhaitez faire. Mais dans des cas compliqués ou s'il y a plusieurs arguments de types différents, alors il devient néecessaire de spécifier la version. int main() > pi(3 e(2 fetolth a maximum(pi,e) endl; Le compilateur voit dans ce cas que l'on souhaite utiliser la version double de la fonction. A vous de voir si votre compilateur comprend vos intentions. Si vous 6tes attentif, vous avez peut-étre remarqué que, pour les arguments, j'ai remplacé le passage par valeur par des références constantes. En effet, on ne sait pas quel type l'utilisateur va utiliser avec notre fonction maximum. La taille en mémoire de ce type sera peut-étre trés grande : on passe donc une référence constante pour éviter une copie cofiteuse et inutile. OU mettre la fonction ? Habituellement, un programme est subdivisé en plusieurs fichiers que l'on classe en deux catégories : 1. Les fichiers de code (les .cpp ) 2. Les fichiers d'en-téte (les .hpp ) Généralement, on met le prototype de la fonction dans un .hpp etladéfinition dans le .cpp Pour les fonctions templates, c'est différent. TOUT doit obligatoirement se trouver dans le fichier .hpp__, sinon votre programme ne pourra pas compiler. Je le répéte car c'est une erreur classique : le prototype ET la définition d'une fonction template doivent obligatoirement se trouver dans un fichier d'en-téte. Tous les types sont-ils utilisables ? Le compilateur génére toutes les fonctions nécessaires. Cependant, il y a quand méme une contrainte ici : le type que l'on passe a la fonction doit posséder un operator> Par exemple, on ne peut pas utiliser cette fonction avecun Personnage ouun Magicien des chapitres précédents : ils ne possédent pas de surcharge de > . Tant mieux, puisque prendre le maximum de deux personnages n‘a pas de sens ! Les contraintes dépendent des fonctions que vous écrivez : * Si vous utilisez 'opérateur + dans la fonction, alors il faut que l'objet passé en argument surcharge cet opérateur. « Si vous effectuez une copie dans la fonction, alors l'objet doit posséder un constructeur de copie, etc. Découvrez des fonctions plus os compliquées Vous avez appris a écrire une fonction qui calcule la moyenne d'un tableau. A nouveau, les operations a effectuer sont les mémes quel que soit le type contenu. Ecrivons donc cette fonction sous forme de template. Voici ma version : cpp DMG ane) CS iran Fe BED) { Sette ¢ i(0); i au lieu d'un tableau statique ; ¢ écrire une fonction template renvoyant un nombre aléatoire d'un type danné Spécialisez la fonction template Pour l'instant, nous n'avons essayé la fonction maximum() qu'avec des types de base. Essayons-la donc avec une chaine de caracteres goes int main() { hd DeSsut aah Le résultat de ce petit programme est eis [ema -4 Uopérateur < pour les chaines de caractéres compare suivant l'ordre lexicographique. Mais imaginons (comme précédemment) que le critére de comparaison qui nous intéresse soit la longueur de la chaine. Cela se fait en spécialisant la fonction template. Comprenez le principe de la spécialisation La spécialisation emploie la syntaxe suivante Dae U a ven iets Uys eet) Vous remarquerez deux choses Vous remarquerez deux choses 1. La premiére ligne ne comporte aucun type entre < et > 2. Le prototype de la fonction utilise cette fois le type que l'on veut, et non plus le type générique T Avec cette spécialisation, on obtient le comportement voulu : cpp int main() af cout Ce qui donne: Le plus grand est: eleph La seule difficulté de la spécialisation est la syntaxe, qui commence par la ligne template<> . Si vous vous souvenez de cela, vous savez tout. Vous pouvez évidemment spécialiser la fonction pour plusieurs types différents. Il vous faudra alors créer une spécialisation par type. Pensez a I'ordre des fonctions Pour pouvoir compiler et avoir le comportement voulu, votre programme devra étre organisé d'une maniére speciale. II faut respecter un ordre particulier : 1. La fonction générique 2. Les fonctions spécialisées. Lordre est essentiel. Lors de la compilation, le compilateur cherche une fonction spécialisée. S'il n'en trouve pas, alors il utilise la fonction générique déclarée au-dessus. Réalisezlesclasses template v Voyons maintenant comment réaliser des classes template, c'est-a-dire des classes dont le type des arguments peut varier. Prenons un exemple : lorsque l'on veut dessiner des choses a l'écran, on utilise quelques formes de base qui servent a décomposer les objets plus complexes. Lune de ces formes est le rectangle. Voici les propriétés (ou les attributs) d'un rectangle : 1. Quatre cétés. 2. Une surface. 3. Un périmétre. Les deux derniers éléments peuvent étre calculés si l'on connait sa longueur et sa largeur. Maintenant, voyons quelles actions on peut associer a un rectangle (vérifier si un point est contenu dans le rectangle, déplacer le rectangle...). On pourrait donc modéliser notre classe comme ceci : Rectangle # gauche # droite # haut # bas + deplacer() contient() On considére ici un rectangle paralléle aux bords de l'écran, ce qui permet de simplifier les positions en utilisant un seul et unique nombre par céte. Indiquez le type des attributs Maintenant que nous avons modélisé la classe, il est temps de réfléchir aux types des attributs, en l'occurrence la position des cétés : 1. Option 1: si l'on veut avoir une bonne précision, il faut utiliser des double oudes float 2. Option 2 : si par contre on consideére que, de toute fagon, l'écran est composé de pixels, on peut se dire que l'utilisation d' int est largement suffisante. Les deux options sont possibles, et on peut trés bien avoir besoin des deux approches dans un seul et méme programme Et c'est la que vous devriez tous me dire : "Mais alors, utilisons donc des templates !". Vous avez bien raison. Nous allons écrire une seule classe qui pourra étre instanciée par le compilateur avec différents types Créez la classe Je suis sr que vous connaissez la syntaxe méme si je ne vous I'ai pas encore donnée. Comme d'habitude, on déclare un type générique T , puis on déclare notre Classe : Notre type générique est reconnu par le compilateur l'intérieur de la classe. Utilisons-le donc pour déclarer nos quatre attributs template Pree ar sant Dae T m_droite; Samet aan Voila. Il ne nous reste plus qu'a écrire les méthodes. Ecrivez les méthodes Les fonctions les plus simples a écrire sont certainement les accesseurs, qui permettent de connaitre la valeur des attributs. La hauteur d'un rectangle est évidemment la différence entre la position du haut et la position du bas. Comme vous vous en doutez, cette fonction est template, puisque le type de retour de la fonction sera un T Ecrivez une premiere méthode Nous pouvons donc écrire la méthode suivante : c_cpp fe Er Era ae tae hauteur() Car Teh oe oo m_gauche; Cae Partha fe Vous remarquerez qu'il n'y a pas besoin de redéclarer le type template T juste avant la fonction membre, puisque celui que nous avons déclaré avant la classe reste valable pour tout ce qui se trouve a l'intérieur. Et si je veux mettre le corps de ma fonction a l'extérieur de ma classe ? Bonne question. On prend souvent I'habitude de separer le prototype de la définition. Et cela peut se faire aussi ici. Pour cela, on mettra le prototype dans la classe, et la définition a |'extérieur ; mais il faut indiquer a nouveau qu'on utilise un type variable T cpp Oyu s Rectangle{ Pil a ets T hauteur() const; ctangle: :hauteur() const eee ree Vous remarquerez aussi l'utilisation du type template dans le nom de la classe, puisque cette fonction sera instanciée de maniére différente pour chaque T Souvenez-vous que tout doit se trouver dans le fichier .hpp ! Ecrivez une fonction un peu plus complexe Une des fonctions que nous voulions écrire est celle permettant de vérifier si un point est contenu dans le rectangle ou pas. Pour cela, on doit passer un point $$$(2x; y)$$$ en argument a la fonction Le type de ces arguments doit évidemment étre T de sorte que l'on puisse comparer les coordonnées sans avoir de conversions. cpp Fees a a eae et ab teed see ae oe eS return (x >= m_gauche) && (x <= m_droite) Sa meat Mario Vous remarquerez 4 nouveau l'absence de redéfinition dutype T Quoi, je me répete ? C'est srement que cela devient clair pour vous. @ Traitez le cas du constructeur Il ne nous reste plus qu'a traiter le cas du constructeur. A nouveau, rien de bien compliqué, on utilise simplement le type T défini avant la classe : s Rectangle{ re eta [tee RAG ce Pe PD) aera tes (droite), Catach GE Et comme pour toutes les autres méthades, on peut définir le constructeur a |'extérieur de la classe. Vous étes bientdt incollable sur le sujet, je vous laisse donc essayer seu On pourrait ajouter une fonction appelée dans le constructeur, qui vérifie que le haut se trouve bien au-dessus du bas, et de méme pour droite et gauche. Finalement, voyons comment utiliser cette classe. Instanciez une classe template \I fallait bien y arriver un jour ! Comment crée-t-on un objet de la classe Rectangle ? En fait, je suis sdr que vous le savez déja. Cela fait longtemps que vous créez des objets a partir de la classe template vector ou map Sil'on veut un Rectangle composéde double , on devra écrire : cpp Finalement, voyons comment utiliser cette classe. Instanciez une classe template I fallait bien y arriver un jour ! Comment crée-t-on un objet de la classe Rectangle ? En fait, je suis sir que vous le savez déja. Cela fait longtemps que vous créez des objets a partir de la classe template vector ou map Sil'onveut un Rectangle composéde double , on devra écrire : int main() a Pree a See 0 oem a Cone 2 Lutilisation des fonctions se fait ensuite comme d'habitude : cpp int main() rt [tera shee een tea) 2 footie [Link]() ab Pour terminer ce chapitre, je vous propose d'ajouter quelques méthodes a cette classe. Je vous parlais d'une méthode deplacer() qui change la position du rectangle. Essayez aussi d'écrire les méthodes surface() et perimetre() En résumé v « Les templates sont utilisés pour créer différentes versions d'une fonction ou d'une classe pour des types différents. ¢ Pour créer une fonction ou une classe template, il faut déclarer un type générique en utilisant la syntaxe template ¢ Pour utiliser une fonction ou une classe template, on indique le type désiré entre les chevrons < et | ¢ ll est possible de spécialiser les templates pour leur imposer un comportement particulier pour certains types. Maintenant, vous pouvez simplifier (ensemble de vos programmes en C++ grace aux templates ; ils seront vos alliés pour éviter la duplication du code. Sinon, jai une bonne nouvelle : cest bientét la fin du cours. Allez ,un dernier "petit" chapitre, et je vous laisse tranquille. Si l'apprentissage du C++ vous a plu, je vous propose de vous présenter comment aller encore plus loin !

Vous aimerez peut-être aussi