introduction
Dans la programmation C++ de base, le type de données, e.g., int ou char, doit être indiqué dans une déclaration ou une définition. Une valeur telle que 4 ou 22 ou -5 est un entier. Une valeur telle que 'A' ou 'b' ou 'c' est un caractère. Le mécanisme de modèle permet au programmeur d'utiliser un type générique pour un ensemble de types réels. Par exemple, le programmeur peut décider d'utiliser l'identifiant T pour int ou char. Il est possible qu'un algorithme C++ ait plus d'un type générique. Avec, disons, T pour int ou char, U peut représenter le type float ou pointeur. Une classe, telle que la classe chaîne ou vecteur, est comme un type de données, et les objets instanciés sont comme des valeurs du type de données, qui est la classe spécifiée. Ainsi, le mécanisme de modèle permet également au programmeur d'utiliser un identifiant de type générique pour un ensemble de classes.
Un modèle C++ crée un algorithme indépendant du type de données utilisées. Ainsi, le même algorithme, avec de nombreuses occurrences du même type, peut utiliser différents types à différentes exécutions. Les entités de variable, function, struct et class peuvent avoir des modèles. Cet article explique comment déclarer des modèles, comment définir des modèles et comment les appliquer en C++. Vous devez déjà avoir une connaissance des entités susmentionnées pour comprendre les sujets abordés dans cet article.
Les types
Scalaire
Les types scalaires sont void, bool, char, int, float et pointer.
Les classes en tant que types
Une classe particulière peut être considérée comme un type et ses objets comme des valeurs possibles.
Un type générique représente un ensemble de types scalaires. La liste des types scalaires est longue. Le type int, par exemple, a d'autres types connexes, tels que short int, long int, etc. Un type générique peut aussi représenter un ensemble de classes.
Variable
Voici un exemple de déclaration et de définition de modèle :
modèleTpi = 3.14 ;
Avant de continuer, notez que ce type d'instruction ne peut pas apparaître dans la fonction main() ni dans aucune portée de bloc. La première ligne est la déclaration template-head, avec le nom de type générique choisi par le programmeur, T. La ligne suivante est la définition de l'identifiant, pi, qui est de type générique, T. La précision, que le T soit un int ou un float ou un autre type, peut être effectuée dans la fonction C++ main() (ou une autre fonction). Une telle précision se fera avec la variable pi, et non T.
La première ligne est la déclaration template-head. Cette déclaration commence par le mot réservé, modèle, puis les chevrons ouverts et fermés. Dans les crochets angulaires, il y a au moins un identificateur de type générique, tel que T, au-dessus. Il peut y avoir plus d'un identificateur de type générique, chacun étant précédé du mot réservé, typename. Ces types génériques dans cette position sont appelés paramètres de modèle.
L'instruction suivante peut être écrite dans main() ou dans toute autre fonction :
cout << piEt la fonction afficherait 3.14. L'expression pi
Lors de la spécialisation, le type de données choisi, tel que float, est placé entre crochets angulaires après la variable. S'il y a plus d'un paramètre de modèle dans la déclaration template-head, il y aura un nombre correspondant de types de données dans le même ordre dans l'expression de spécialisation.
Lors de la spécialisation, un type est appelé argument de modèle. Ne pas confondre entre ceci et l'argument de fonction pour l'appel de fonction.
Type par défaut
Si aucun type n'est donné à la spécialisation, le type par défaut est supposé. Ainsi, à partir de l'expression suivante :
modèleUpi = « amour » ;
l'affichage de :
cout << pi<> << '\n';
est « amour » pour le pointeur constant vers char. Notez dans la déclaration que U = const char*. Les crochets angulaires seront vides à la spécialisation (aucun type donné) ; le type réel est considéré comme un pointeur const vers char, le type par défaut. Si un autre type était nécessaire lors de la spécialisation, le nom du type serait écrit entre crochets angulaires. Lorsque le type par défaut est souhaité lors de la spécialisation, la répétition du type dans les crochets angulaires est facultative, je.e., les équerres peuvent être laissées vides.
Remarque : le type par défaut peut toujours être modifié lors de la spécialisation en ayant un type différent.
structure
L'exemple suivant montre comment un paramètre de modèle peut être utilisé avec une structure :
modèleT Jean = 11 ;
T Pierre = 12 ;
T Marie = 13 ;
T Joie = 14 ;
;
Ce sont les âges des élèves dans une classe (classe). La première ligne est la déclaration du modèle. Le corps entre accolades est la définition même du modèle. Les âges peuvent être affichés dans la fonction main() avec les éléments suivants :
Âgecout << grade7.John << " << grade7.Mary << '\n';
La sortie est : 11 13. La première déclaration ici effectue la spécialisation. Notez comment il a été fait. Il donne également un nom à un objet de la structure : grade7. La deuxième instruction a des expressions d'objet struct ordinaires. Une structure est comme une classe. Ici, Ages est comme un nom de classe, tandis que grade7 est un objet de la classe (struct).
Si certains âges sont des entiers et d'autres des flottants, alors la structure a besoin de deux paramètres génériques, comme suit :
modèleT Jean = 11 ;
U Pierre = 12.3 ;
T Marie = 13 ;
U joie = 14.6 ;
;
Un code pertinent pour la fonction main() est le suivant :
Âgecout << grade7.John << " << grade7.Peter << '\n';
La sortie est : 11 12.3. A la spécialisation, l'ordre des types (arguments) doit correspondre à l'ordre des types génériques dans la déclaration.
La déclaration du modèle peut être séparée de la définition comme suit :
modèleT Jean ;
U Pierre ;
T Marie ;
U joie;
;
Âge
Le premier segment de code est purement une déclaration d'un modèle (il n'y a pas d'affectations). Le deuxième segment de code, qui n'est qu'un énoncé, est la définition de l'identifiant, grade7. La partie gauche est la déclaration de l'identifiant, grade7. Le côté droit est la liste d'initialisation, qui attribue les valeurs correspondantes aux membres de la structure. Le deuxième segment (instruction) peut être écrit dans la fonction main(), tandis que le premier segment reste en dehors de la fonction main().
Non-Type
Des exemples de types non-données incluent les types int, pointeur vers objet, pointeur vers fonction et auto. Il existe d'autres non-types, que cet article ne traite pas. Un non-type est comme un type incomplet, dont la valeur est donnée ultérieurement et ne peut être modifiée. En paramètre, il commence par un non-type particulier, suivi d'un identifiant. La valeur de l'identifiant est donnée ultérieurement, lors de la spécialisation, et ne peut plus être modifiée (comme une constante, dont la valeur est donnée ultérieurement). Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
modèle
T Jean = N ;
U Pierre = 12.3 ;
T Marie = N;
U joie = 14.6 ;
;
int main()
Âge
cout << grade7.John << " << grade7.Joy << '\n';
renvoie 0 ;
A la spécialisation, le premier type, int, dans les chevrons est là plus pour la formalité, pour s'assurer que le nombre et l'ordre des paramètres correspondent au nombre et à l'ordre des types (arguments). La valeur de N a été donnée à la spécialisation. La sortie est : 11 14.6.
Spécialisation partielle
Supposons qu'un modèle a quatre types génériques et que, parmi les quatre types, il existe un besoin de deux types par défaut. Ceci peut être réalisé en utilisant la construction de spécialisation partielle, qui n'emploie pas l'opérateur d'affectation. Ainsi, la construction de spécialisation partielle donne des valeurs par défaut à un sous-ensemble de types génériques. Cependant, dans le schéma de spécialisation partielle, une classe de base (struct) et une classe de spécialisation partielle (struct) sont nécessaires. Le programme suivant illustre cela pour un type générique sur deux types génériques :
#inclureen utilisant l'espace de noms std ;
//classe de modèle de base
modèle
struct Âges
;
//spécialisation partielle
modèle
struct Âges
T1 Jean = 11 ;
flotteur Pierre = 12.3 ;
T1 Marie = 13 ;
flotter Joie = 14.6 ;
;
int main()
Âge
cout << grade7.John << " << grade7.Joy << '\n';
renvoie 0 ;
Identifier la déclaration de classe de base et sa définition de classe partielle. La déclaration template-head de la classe de base a tous les paramètres génériques nécessaires. La déclaration template-head de la classe de spécialisation partielle a uniquement le type générique. Il y a un ensemble supplémentaire de crochets angulaires utilisés dans le schéma qui vient juste après le nom de la classe dans la définition de spécialisation partielle. C'est ce que fait réellement la spécialisation partielle. Il a le type par défaut et le type non par défaut, dans l'ordre écrit dans la classe de base. Notez que le type par défaut peut toujours recevoir un type différent dans la fonction main().
Le code pertinent dans la fonction main() peut être le suivant :
Âgecout << grade7.John << " << grade7.Joy << '\n';
La sortie est : 11 14.6.
Pack de paramètres de modèle
Un pack de paramètres est un paramètre de modèle qui accepte zéro ou plusieurs types génériques de modèle pour les types de données correspondants. Le paramètre du pack de paramètres commence par le mot réservé typename ou class. Ceci est suivi de trois points, puis de l'identifiant du pack. Le programme suivant illustre comment un pack de paramètres de modèle peut être utilisé avec une structure :
#inclureen utilisant l'espace de noms std ;
modèle
int Jean = 11 ;
flotteur Pierre = 12.3 ;
int Marie = 13;
flotter Joie = 14.6 ;
;
int main()
Âge
cout << gradeB.John << " << gradeB.Mary << '\n';
Âge
cout << gradeC.Peter << " << gradeC.Joy << '\n';
Âge
cout << gradeD.John << " << gradeD.Joy << '\n';
Âges<> gradeA ; //comme par défaut
cout << gradeA.John << " << gradeA.Joy << '\n';
renvoie 0 ;
La sortie est :
11 1312.3 14.6
11 14.6
11 14.6
Modèles de fonction
Les fonctionnalités de modèle mentionnées ci-dessus s'appliquent de la même manière aux modèles de fonction. Le programme suivant montre une fonction avec deux paramètres de modèle génériques et trois arguments :
#inclureen utilisant l'espace de noms std ;
modèle
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
int main()
func(12, '$', "500");
renvoie 0 ;
La sortie est la suivante :
Il y a 12 livres d'une valeur de 500 $ dans le magasin.
Séparation du prototype
La définition de la fonction peut être séparée de son prototype, comme le montre le programme suivant :
#inclureen utilisant l'espace de noms std ;
modèle
modèle
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
int main()
func(12, '$', "500");
renvoie 0 ;
Remarque : La déclaration du modèle de fonction ne peut pas apparaître dans la fonction main() ou dans toute autre fonction.
Surcharge
La surcharge de la même fonction peut avoir lieu avec différentes déclarations template-head. Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
modèle
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
modèle
cout << "There are " << no << " books worth $" << str << " in the store." << '\n';
int main()
func(12, '$', "500");
func(12, "500");
renvoie 0 ;
La sortie est :
Il y a 12 livres d'une valeur de 500 $ dans le magasin.
Il y a 12 livres d'une valeur de 500 $ dans le magasin.
Modèles de cours
Les fonctionnalités des modèles mentionnés ci-dessus s'appliquent de la même manière aux modèles de classe. Le programme suivant est la déclaration, la définition et l'utilisation d'une classe simple :
#inclureen utilisant l'espace de noms std ;
classe TheCla
Publique:
nombre entier ;
char ch statique;
void func (char cha, const char *str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
amusement du vide statique (char ch)
si (ch == 'a')
cout << "Official static member function" << '\n';
;
int main()
TheCla obj;
obj.nombre = 12 ;
obj.func('$', "500");
renvoie 0 ;
La sortie est la suivante :
Il y a 12 livres d'une valeur de 500 $ dans le magasin.
Le programme suivant est le programme ci-dessus avec une déclaration template-head :
#inclureen utilisant l'espace de noms std ;
modèle
Publique:
T num;
statique U ch;
void func (U cha, const char *str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
amusement du vide statique (U ch)
si (ch == 'a')
cout << "Official static member function" << '\n';
;
int main()
La Cla
obj.nombre = 12 ;
obj.func('$', "500");
renvoie 0 ;
Au lieu du nom de type de mot dans la liste des paramètres du modèle, la classe de mots peut être utilisée. Notez la spécialisation dans la déclaration de l'objet. Le rendu est toujours le même :
Il y a 12 livres d'une valeur de 500 $ dans le magasin.
Déclaration de séparation
La déclaration du modèle de classe peut être séparée du code de classe, comme suit :
modèlemodèle
Publique:
T num;
statique U ch;
void func (U cha, const char *str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
amusement du vide statique (U ch)
si (ch == 'a')
cout << "Official static member function" << '\n';
;
Traiter avec les membres statiques
Le programme suivant montre comment accéder à une donnée membre statique et à une fonction membre statique :
#inclureen utilisant l'espace de noms std ;
modèle
Publique:
T num;
statique U ch;
void func (U cha, const char *str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
amusement du vide statique (U cha)
si (ch == 'a')
cout << "Official static member function" << cha << '\n';
;
modèle
int main()
La Cla
renvoie 0 ;
L'attribution d'une valeur à un membre de données statique est une déclaration et ne peut pas être dans main(). Notez l'utilisation et les positions des types génériques et du type générique de données dans l'instruction d'affectation. De plus, notez que la fonction membre de données statiques a été appelée dans main(), avec les types de données de modèle réels. La sortie est la suivante :
Fonction membre statique officielle.
Compilation
La déclaration (en-tête) et la définition d'un modèle doivent être dans un seul fichier. C'est-à-dire qu'ils doivent être dans la même unité de traduction.
Conclusion
Les modèles C++ créent un algorithme indépendant du type de données utilisées. Les entités de variable, function, struct et class peuvent avoir des modèles, qui impliquent une déclaration et une définition. La création d'un modèle implique également une spécialisation, c'est-à-dire lorsqu'un type générique prend un type réel. La déclaration et la définition d'un modèle doivent toutes deux être dans une unité de traduction.