Définir un modèle de fonction. Prototype de modèle de fonction. Création d'un modèle de fonction simple
Nous avons déjà envisagé un outil tel que les modèles en C++ lorsque nous avons créé . La raison pour laquelle vous devriez utiliser des modèles a été écrite dans l'article avec les modèles de fonctions. Nous y avons examiné les dispositions de base des modèles en C++. Souvenons-nous d'eux.
Tout modèle commence par le mot modèle, qu'il s'agisse d'un modèle de fonction ou d'un modèle de classe. Le mot-clé du modèle est suivi de crochets angulaires -< >, qui répertorie une liste de paramètres de modèle. Chaque paramètre doit être précédé du mot réservé class ou typename . L'absence de ces mots-clés sera interprétée par le compilateur comme . Quelques exemples de déclarations de modèles :
Modèle
Modèle
Modèle
Le mot-clé typename indique au compilateur que le modèle utilisera un type de données intégré tel que : int , double , float , char , etc. Et le mot-clé class indique au compilateur que le modèle de fonction utilisera des types de données personnalisés comme paramètre , c'est-à-dire des cours. Mais ne confondez en aucun cas un paramètre de modèle avec un modèle de classe. Si nous devons créer un modèle de classe avec un paramètre de type int et char , le modèle de classe ressemblera à ceci :
Modèle
où T est un paramètre de modèle de classe qui peut accepter n'importe quel type de données intégré, ce dont nous avons besoin.
Et si le paramètre du modèle de classe doit être d'un type personnalisé, tel que Array , où Array est une classe qui décrit un tableau, le modèle de classe ressemblera à ceci :
Modèle
Il est préférable que vous compreniez cela dès le début afin qu'aucune erreur ne survienne plus tard, même si le modèle de classe est écrit correctement.
Créons un modèle de classe Stack, où , qui stocke des éléments de données similaires. Vous pouvez pousser et insérer des données sur la pile. Un élément ajouté à la pile est placé en haut de la pile. Les éléments de la pile sont supprimés en commençant par le haut. Dans le modèle de classe Stack, vous devez créer les méthodes principales :
- Pousser— ajouter un élément à la pile ;
- Populaire- supprimer un élément de la pile
- imprimerStack— afficher la pile sur l'écran ;
Implémentons donc ces trois méthodes, et à la fin nous obtiendrons la classe la plus simple qui implémente le fonctionnement de la structure de pile. N'oubliez pas les constructeurs et les destructeurs. Voir le code ci-dessous.
#include "stdafx.h" #include
// code Code :: Blocs
// Code Dev-C++
#inclure
::~Stack() ( delete stackPtr; // supprime la pile ) // l'élément est une fonction de la classe Stack permettant de placer un élément sur la pile // la valeur de retour est vraie, l'opération s'est terminée avec succès // faux, l'élément n'a pas été ajouté au modèle de pile
::push(const T value) ( if (top == size - 1) return false; // la pile est pleine top++; stackPtr = value; // pousser l'élément sur la pile return true; // réussite de l'opération) // fonction de l'élément Classe Stack pour supprimer un élément de la pile // la valeur de retour est vraie, l'opération s'est terminée avec succès // faux, la pile est un modèle vide
Pour lier chaque élément de fonction à un modèle de classe, comme d'habitude nous utilisons l'opération de résolution de portée binaire - :: avec le nom du modèle de classe - Stack
Notez la déclaration de l'objet myStack du modèle de classe Stack dans la fonction principale, ligne 24. Les crochets angulaires doivent indiquer explicitement le type de données utilisé ; cela n'était pas nécessaire dans les modèles de fonctions. Ensuite, main exécute certaines fonctions qui démontrent le fonctionnement du modèle de classe Stack. Le résultat du programme est présenté ci-dessous.
On pousse les éléments sur la pile : 12 3456 768 5 4564 |4564 | 5 | 768 |3456 | 12 Supprimez deux éléments de la pile : | 0 | 0 | 768 |3456 | 12
J'étais sur le point d'écrire un texte sur toutes sortes de structures de données intéressantes, puis il s'est avéré que nous n'avions pas encore examiné plusieurs fonctionnalités très importantes du C++. Les modèles en font partie.
Les modèles sont un outil très puissant. Les fonctions et classes de modèles peuvent grandement simplifier la vie d'un programmeur et lui faire gagner énormément de temps, d'efforts et de nerfs. Si vous pensez que les modèles ne sont pas un sujet très important à étudier, sachez que vous vous trompez.
Fonctions du modèle
Un exemple simple de fonction de modèle :
code en langage C++ Tapez carré (Type a) ( Type b; b = a*a; return b; ) int x = 5; int je; je = carré(5); flottant y = 0,5 ; flotter f; f = carré(y);
Si nous créions des fonctions à l’ancienne, nous devrions alors écrire deux fonctions différentes : pour le type int et pour le type float. Et si vous aviez besoin de la même fonction en utilisant d’autres types, vous devrez la réécrire. À l'aide de modèles, vous pouvez vous limiter à une seule instance d'une fonction, laissant tout le sale boulot au compilateur.
Au lieu d'utiliser un type spécifique, la fonction utilise un type paramétrique (ou, en d'autres termes, un argument de modèle). Ici, j'ai appelé le type paramétrique l'identifiant Type. Cet identifiant apparaît trois fois dans une fonction : la valeur de retour, l'argument de la fonction et la définition de la variable s. Autrement dit, Type est utilisé comme n’importe quel type normal.
Mais pour que le code fonctionne, il faut ajouter la ligne suivante avant la fonction (j'ai montré plusieurs options de syntaxe, elles fonctionnent toutes) :
code en langage C++ modèle
Ainsi, la fonction doit être précédée du modèle de mot-clé, et entre crochets vous devez spécifier le nom du type paramétrique avec la classe de mot-clé. Au lieu du mot-clé class, vous pouvez utiliser type - en général, il n'y a aucune différence.
L'identifiant de type paramétrique peut également être n'importe quoi. Nous utiliserons souvent ceux-ci : TypeA, TypeB, Datatype, T.
Remarque importante: Les fonctions de modèle doivent avoir un argument pour que le compilateur puisse déterminer le type à utiliser.
Vous pouvez utiliser plusieurs types paramétriques dans les modèles, et bien sûr, vous pouvez mélanger des types paramétriques avec des types standard (il vous suffit de veiller à la conversion de type correcte). Je vais donner un exemple qui utilise deux types paramétriques TypeA, TypeB et le type de base int :
code en langage C++ modèle
Mais les fonctions de modèle ne sont pas la chose la plus intéressante que nous examinerons aujourd'hui.
Classes de modèles
En général, les classes modèles sont créées de la même manière que les fonctions modèles : le mot-clé template est écrit avant le nom de la classe. Examinons les classes de modèles en utilisant l'exemple d'une pile :
code en langage C++ modèle
partagé une pile de dix éléments. Ces éléments peuvent être de n’importe quel type, nous en parlerons ci-dessous.
La seule chose sur laquelle je souhaite attirer votre attention est la définition des fonctions push et pop. La fonction push est définie à l'intérieur de la classe et la fonction pop est définie à l'extérieur. Pour toutes les fonctions déclarées en dehors de la classe, le mot-clé template doit être spécifié. L'expression avant le nom de la fonction est la même que celle avant le nom de la classe.
Voyons maintenant comment travailler avec les classes modèles :
code en langage C++ empiler
Lors de la création d'un objet, après le nom de la classe, vous devez mettre des crochets angulaires pour indiquer le type souhaité. Après cela, les objets sont utilisés comme nous en avons l’habitude.
Les classes de modèles ont une fonctionnalité étonnante : en plus des types standard, elles peuvent également fonctionner avec des types personnalisés. Regardons un petit exemple. Pour ce faire, définissons une classe de guerrier simple :
code en langage C++ guerrier de classe ( public: int santé; guerrier () : santé(0) () ); empiler
Regardez, maintenant vous pouvez mettre des variables de type guerrier sur des piles !!! Vous ne me croyez peut-être pas, mais c'est très cool ! Vous pouvez voir à quel point c'est cool lorsque nous créons des graphiques et des arbres basés sur des listes.
Tout est conforme aux modèles pour l'instant. Nous examinerons plus tard des cas plus complexes d'utilisation de classes modèles.
Une fonction modèle définit un ensemble général d'opérations qui seront appliquées à différents types de données. Grâce à ce mécanisme, il est possible d’appliquer certains algorithmes généraux à un large éventail de données. Comme vous le savez, de nombreux algorithmes sont logiquement les mêmes quel que soit le type de données avec lesquels ils fonctionnent. Par exemple, l'algorithme de tri rapide est le même pour un tableau d'entiers et un tableau de nombres à virgule flottante. Seul le type de données à trier diffère. En créant une fonction générique, vous pouvez définir l'essence de l'algorithme quel que soit le type de données. Après cela, le compilateur génère automatiquement le code correct pour le type de données pour lequel cette implémentation particulière de la fonction est créée au stade de la compilation. Essentiellement, lorsqu'une fonction modèle est créée, elle crée une fonction qui peut automatiquement se surcharger.
Les fonctions de modèle sont créées à l'aide du mot-clé template. Le sens habituel du mot « modèle » reflète assez pleinement son utilisation en C++. Un modèle est utilisé pour créer le squelette d'une fonction, laissant les détails d'implémentation au compilateur. La forme générale d'une fonction modèle est la suivante :
modèle
{
// corps de la fonction
}
Ici, ptype est un paramètre de type, un « espace réservé » pour le nom du type de données utilisé par la fonction. Ce paramètre de type peut être utilisé dans une définition de fonction. Cependant, il ne s'agit que d'un "espace réservé" qui sera automatiquement remplacé par le compilateur par le type de données réel lorsqu'une version particulière de la fonction est créée.
Vous trouverez ci-dessous un court exemple qui crée une fonction de modèle comportant deux paramètres. Cette fonction modifie les valeurs de ces paramètres entre elles. Étant donné que le processus général d'échange de valeurs entre deux variables ne dépend pas de leur type, il peut être naturellement implémenté à l'aide d'une fonction modèle.
// exemple de modèle de fonction
#inclure
// modèle de fonction
modèle
{
Température X ;
température = a;
une = b;
b = température ;
}
int principal()
{
int je = 10, j = 20 ;
flotteur x=10,1, y= 23,3 ;
char a="x", b="z";
cout<< "Original i, j: " << i << " " << j << endl;
cout<< "Original x, y: " << x << " " << у << endl;
cout<< "Original a, b: " << a << " " << b << endl;
échanger(je, j); // échange d'entiers
échanger(x, y); // échange de valeurs réelles
échanger(a, b); // échange de caractères
cout<< "Swapped i, j: " << i << " " << j << endl;
cout<< "Swapped x, y: " << x << " " << у << endl;
cout<< "Swapped a, b: " << a << " " << b << endl;
renvoie 0 ;
}
Regardons de plus près ce programme. Doubler
Modèle
Indique au compilateur qu'un modèle est en cours de génération. Ici, X est le modèle de type utilisé comme paramètre de type. Vient ensuite la déclaration de la fonction swap(), utilisant le type de données X pour les paramètres qui échangeront des valeurs. Dans la fonction main(), la fonction swap() est appelée avec trois types de données différents qui lui sont transmis : des entiers, des nombres à virgule flottante et des caractères. Étant donné que la fonction swap() est une fonction modèle, le compilateur créera automatiquement trois versions différentes de la fonction swap() : une pour travailler avec des entiers, une autre pour travailler avec des nombres à virgule flottante et enfin une troisième pour travailler avec des variables de caractères.
Vous pouvez prototyper un modèle de fonction en le pré-déclarant.
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :
void Sort (tableau T, taille Tsize);
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :
Tmax(T,T);
Type max (Type a, Type b)
si (a > b) renvoie a ; sinon, retourne b;
Utilisation d'un modèle de fonction Un modèle de fonction décrit comment une fonction particulière peut être construite sur la base d'un ou plusieurs types réels. Le compilateur crée automatiquement un corps de fonction représentatif pour les types spécifiés dans l'appel. Ce processus est appelé spécification
.
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :
Cela se produit lorsqu'une fonction de modèle est appelée.
Par exemple, dans l'exemple suivant, la fonction min() est instanciée deux fois : une fois avec le type int et une fois avec le type double :< b) return a; else return b;
Tapez min (Type a, Type b)
si (un
int x = 4, y = 5, z ;
doublet = 6,56, r = 3,07, p ;
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :
Cela se produit lorsqu'une fonction de modèle est appelée.
Par exemple, dans l'exemple suivant, la fonction min() est instanciée deux fois : une fois avec le type int et une fois avec le type double :< b) return a; else return b;
Spécialisation des modèles de fonctions
Une fonction de modèle spécialisée est une fonction régulière dont le nom est le même que celui de la fonction dans le modèle, mais qui est définie pour des paramètres de types spécifiques.
Les fonctions de modèle spécialisées sont définies lorsqu'un modèle générique ne convient pas à un type de données particulier. Par exemple, la fonction modèle min
Vous pouvez alors accéder à une telle fonction de la même manière qu’une fonction modèle :
int i1 = 3, i2 = 5 ;
cout<< “max int = ” << max(i1, i2) << endl;
char* s1 = « Aigle royal » ;
char* s2 = « Faucon pèlerin » ;
cout<< “max str = “ << max(s1, s2) << endl;
Modèles de cours
Modèle de classe donne une définition générale d'une famille de classes utilisant des types ou des constantes arbitraires. Le motif définit membres de données de modèle Et fonctions des membres du modèle.
Une fois qu'un modèle de classe a été défini, vous pouvez demander au compilateur de générer une nouvelle classe basée sur celui-ci pour un type ou une constante spécifique.
Cette déclaration informe le compilateur de la présence du modèle et informe également le compilateur des paramètres attendus. Par exemple, le prototype du modèle de fonction Sort ressemblerait à ceci :<<список аргументов шаблона>>
Syntaxe du modèle de classe<имя класса>
<тело класса>
classe
Le mot-clé template est suivi d'un ou plusieurs arguments (paramètres) entourés de crochets angulaires et séparés par des virgules. Chaque argument peut être un mot-clé de classe suivi d'un identifiant indiquant le type paramétré.
La liste des arguments du modèle ne peut pas être vide. Vient ensuite la définition de la classe. C'est similaire à la définition d'une classe normale, sauf qu'elle utilise une liste d'arguments de modèle. Les paramètres de modèle constitués du mot-clé class suivi d'un identifiant sont souvent appelés
paramètres de type
.
En d’autres termes, ils informent le compilateur que le modèle attend un type comme argument.
En d’autres termes, les modèles de fonctions sont des instructions selon lesquelles des versions locales d’une fonction basée sur un modèle sont créées pour un ensemble spécifique de paramètres et de types de données.< count; ix++) cout << array << " "; cout << endl; } void printArray(const double * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; } void printArray(const float * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; } void printArray(const char * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; }
En fait, les modèles de fonctions sont un outil puissant en C++ qui facilite grandement le travail d'un programmeur. Par exemple, nous devons programmer une fonction qui afficherait les éléments d’un tableau. La tâche n'est pas difficile ! Mais pour écrire une telle fonction, il faut connaître le type de données du tableau que l’on va afficher à l’écran. Et puis ils nous disent : il y a plus d'un type de données, nous voulons que la fonction génère des tableaux de types int, double, float et char.
Et si vous exécutez le programme avec ces fonctions, il fonctionnera correctement. Le compilateur lui-même déterminera quelle fonction utiliser lors de l'appel.
Comme vous pouvez le constater, il y avait beaucoup de code pour une opération aussi simple. Et si nous devions le programmer en fonction. Il s'avère que pour chaque type de données, vous devrez créer votre propre fonction. C'est-à-dire que vous comprenez vous-même que le même code sera en plusieurs exemplaires, cela ne nous est d'aucune utilité. C'est pourquoi C++ a mis au point un tel mécanisme : les modèles de fonctions.
Nous créons un modèle dans lequel nous décrivons tous les types de données. De cette façon, la source ne sera pas encombrée de lignes de code inutiles. Ci-dessous, nous examinerons un exemple de programme avec un modèle de fonction. Rappelons donc la condition : « programmer une fonction qui afficherait les éléments du tableau ».
// code Code :: Blocs
// Code Dev-C++
#inclure
Veuillez noter que le code a été réduit de 4 fois, puisqu'une seule instance de la fonction est déclarée dans le programme - le modèle. Dans main, j'ai déclaré plusieurs tableaux - quatre, pour les types de données : int, double, float, char. Après quoi, aux lignes 26, 28, 30, 32, la fonction printArray est appelée pour différents tableaux. Le résultat du programme est présenté ci-dessous.
Modèle d'affichage d'un tableau à l'écran Tableau de type int : 1 2 3 4 5 6 7 8 9 10 Tableau de type double : 1.2345 2.234 3.57 4.67876 5.346 6.1545 7.7682 Tableau de type float : 1.34 2.37 3.23 4.8 5.879 6. 345 73,434 8,82 9.33 10.4 Tableau de type char : M A R S
Comme vous pouvez le constater, le programme fonctionne correctement, et pour cela il nous suffit de définir une seule fois la fonction printArray sous la forme qui nous est familière. Veuillez noter qu'avant la déclaration de la fonction elle-même, à la ligne 5, il y a l'entrée de modèle suivante
Tous les modèles de fonctions commencent par le mot modèle suivi de crochets qui répertorient les paramètres. Chaque paramètre doit être précédé du mot réservé class ou typename .
Modèle
Modèle
Modèle
Le mot-clé typename indique au compilateur que le modèle utilisera un type de données intégré tel que : int , double , float , char , etc. Et le mot-clé class indique au compilateur que le modèle de fonction utilisera des types de données personnalisés comme paramètre , c'est-à-dire des cours.
Notre modèle de fonction utilisait des types de données intégrés, donc à la ligne 5 nous avons écrit un modèle
// modèle de fonction modèle printArray
La ligne 2 définit un modèle avec un paramètre, T, et ce paramètre aura l'un des types de données intégrés car le mot-clé typename est spécifié.
Ci-dessous, aux lignes 3 à 8, une fonction est déclarée qui répond à tous les critères de déclaration d'une fonction régulière, il y a un en-tête, il y a un corps de fonction, l'en-tête contient le nom et les paramètres de la fonction, tout est comme d'habitude. Mais ce qui transforme cette fonction en modèle de fonction, c'est un paramètre de type de données T , qui est la seule connexion au modèle déclaré précédemment. Si on écrivait
Void printArray(const int * array, int count) ( pour (int ix = 0; ix< count; ix++) cout << array << " "; cout << endl; }
alors ce serait une fonction simple pour un tableau de type int .
Donc, en fait, T n'est même pas un type de données, c'est une place réservée à tout type de données intégré. Autrement dit, lorsque cette fonction est appelée, le compilateur analyse le paramètre de la fonction basée sur un modèle et crée une instance pour le type de données approprié : int, char, etc.
Par conséquent, vous devez comprendre que même si la quantité de code est inférieure, cela ne signifie pas que le programme consommera moins de mémoire. Le compilateur lui-même crée des copies locales de la fonction modèle et, par conséquent, la quantité de mémoire consommée est comme si vous aviez écrit vous-même toutes les instances de fonction, comme c'est le cas en cas de surcharge.
J'espère vous avoir transmis l'idée principale des modèles de fonctions. Pour renforcer le matériel, regardons un autre exemple de programme utilisant un modèle de fonction.
#include "stdafx.h" #include
// code Code :: Blocs
// Code Dev-C++
#inclure
Voici un autre exemple d'utilisation de modèles de fonctions. Le modèle de fonction est déclaré aux lignes 5 à 13. La fonction doit renvoyer la valeur maximale du tableau, donc la valeur de retour est de type T , car le type de données du tableau n'est pas connu à l'avance. À propos, une variable max de type T est déclarée à l'intérieur de la fonction ; la valeur maximale du tableau y sera stockée. Comme vous pouvez le voir, le type de données T est utilisé non seulement pour spécifier les paramètres de fonction, mais également pour indiquer le type de retour, et peut également être librement utilisé pour déclarer n'importe quelle variable dans un modèle de fonction.
Élément maximum d'un tableau de type char : s Élément maximum d'un tableau de type int : 9
Les modèles de fonctions peuvent également être surchargés avec d'autres modèles de fonctions en modifiant le nombre de paramètres transmis à la fonction. Une autre caractéristique de la surcharge est que les fonctions de modèle peuvent être surchargées avec des fonctions normales non-modèles. Autrement dit, le même nom de fonction est spécifié, avec les mêmes paramètres, mais pour un type de données spécifique, et tout fonctionnera correctement.