C++

Comment utiliser les modèles C++

Comment utiliser les modèles C++

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èle
Tpi = 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 << pi << '\n';

Et la fonction afficherait 3.14. L'expression pi décide du type exact de T pour la variable pi. La spécialisation décide du type de données particulier pour le paramètre de modèle. L'instanciation est le processus interne C++ de création du type particulier, tel que float, dans ce cas. Ne pas confondre entre l'instanciation d'un paramètre de modèle et l'instanciation d'une classe. Dans la rubrique modèle, de nombreux types de données peuvent avoir un nom de type générique, tandis que de nombreuses classes peuvent avoir un nom de classe générique. Cependant, le nom de classe générique pour les classes est simplement appelé une classe, et non un nom de classe. En outre, une valeur est à un type de données, tel que l'int, comme un objet instancié est à une classe, telle que la classe String.

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èle
Upi = « 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èle struct Âges

T 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 :

Âge 7e année ;
cout << 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èle struct Âges

T Jean = 11 ;
U Pierre = 12.3 ;
T Marie = 13 ;
U joie = 14.6 ;
 ;

Un code pertinent pour la fonction main() est le suivant :

Âge 7e année ;
cout << 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èle struct Âges

T Jean ;
U Pierre ;
T Marie ;
U joie;
 ;
Âge grade7 = 11, 12.3, 13, 14.6 ;

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 :

#inclure
en utilisant l'espace de noms std ;
modèle struct Âges

T Jean = N ;
U Pierre = 12.3 ;
T Marie = N;
U joie = 14.6 ;
 ;
int main()

Âge 7e année ;
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 :

#inclure
en 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 7e année ;
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 :

Âge 7e année ;
cout << 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 :

#inclure
en utilisant l'espace de noms std ;
modèle struct Âges

int Jean = 11 ;
flotteur Pierre = 12.3 ;
int Marie = 13;
flotter Joie = 14.6 ;
 ;
int main()

Âge catégorie B;
cout << gradeB.John << " << gradeB.Mary << '\n';
Âge grade C ;
cout << gradeC.Peter << " << gradeC.Joy << '\n';
Âge gradeD ;
cout << gradeD.John << " << gradeD.Joy << '\n';
Âges<> gradeA ; //comme par défaut
cout << gradeA.John << " << gradeA.Joy << '\n';
renvoie 0 ;

La sortie est :

11 13
12.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 :

#inclure
en utilisant l'espace de noms std ;
modèle void func (T no, U cha, const char *str )

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 :

#inclure
en utilisant l'espace de noms std ;
modèle void func (T no, U cha, const char *str );
modèle void func (T no, U cha, const char *str )

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 :

#inclure
en utilisant l'espace de noms std ;
modèle void func (T no, U cha, const char *str )

cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';

modèle void func (T no, const char *str )

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 :

#inclure
en 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 :

#inclure
en utilisant l'espace de noms std ;
modèle classe TheCla

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;
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èle classe TheCla;
modèle classe TheCla

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 :

#inclure
en utilisant l'espace de noms std ;
modèle classe TheCla

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 U TheCla::ch = 'a';
int main()

La Cla::amusant('.');
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.

Meilleurs émulateurs de console de jeu pour Linux
Cet article répertorie les logiciels d'émulation de console de jeu populaires disponibles pour Linux. L'émulation est une couche de compatibilité logi...
Meilleures distributions Linux pour les jeux en 2021
Le système d'exploitation Linux a parcouru un long chemin depuis son apparence originale, simple et basée sur le serveur. Ce système d'exploitation s'...
Comment capturer et diffuser votre session de jeu sur Linux
Dans le passé, jouer à des jeux n'était considéré qu'un passe-temps, mais avec le temps, l'industrie du jeu a connu une croissance énorme en termes de...