Templates de classes
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 65 sur 104.
Tout comme les modèles de fonctions vous permettent d'écrire des fonctions indépendantes du type, les modèles de classes vous permettent de créer des classes indépendantes du type. C'est ainsi que les conteneurs comme std::vector fonctionnent : vous définissez la structure une seule fois, et le compilateur génère des versions spécifiques pour chaque type que vous utilisez.
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};
int main() {
Box<int> intBox(42);
Box<std::string> strBox("Hello");
std::cout << intBox.getValue() << std::endl; // 42
std::cout << strBox.getValue() << std::endl; // Hello
}Contrairement aux modèles de fonctions, vous devez spécifier explicitement le type lors de la création d'objets à partir de modèles de classes en utilisant des chevrons. Le compilateur génère ensuite une définition de classe complète pour ce type spécifique.
Les templates de classe peuvent avoir plusieurs paramètres de type et même des paramètres non-type comme des entiers :
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
T& operator[](int index) { return data[index]; }
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Tableau de 5 doubles
arr[0] = 3.14;Lors de la définition de fonctions membres en dehors de la classe, vous devez répéter la déclaration de template :
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Les templates de classe constituent le fondement de la programmation générique en C++, vous permettant d'écrire des structures de données réutilisables qui fonctionnent avec n'importe quel type.
Défi
FacileConstruisons un système de stockage générique en utilisant des modèles de classe (class templates) pour créer un conteneur flexible capable de contenir n'importe quel type de données. Vous organiserez votre classe template dans un fichier d'en-tête et démontrerez sa polyvalence avec différents types dans votre programme principal.
Vous allez créer deux fichiers :
Storage.h: Définissez un modèle de classe appeléStoragequi agit comme un simple conteneur pour une valeur unique avec quelques opérations utiles :Votre template
Storagedoit avoir :- Un membre privé pour contenir la valeur stockée
- Un constructeur qui prend une valeur initiale
- Une méthode
getValue()qui retourne la valeur stockée - Une méthode
setValue()qui met à jour la valeur stockée - Une méthode
isEmpty()qui retournetruesi la valeur est égale à la valeur construite par défaut du type T,falsesinon
Créez également un modèle de classe appelé
Pairavec deux paramètres de type qui stocke deux valeurs liées :- Des membres privés pour la première et la deuxième valeur (de types potentiellement différents)
- Un constructeur qui initialise les deux valeurs
- Des méthodes
getFirst()etgetSecond()pour récupérer chaque valeur - Une méthode
display()qui affiche :(<first>, <second>)
main.cpp: Lisez quatre entrées (chacune sur une ligne séparée) :- Une valeur entière
- Une nouvelle valeur entière
- Une valeur de type chaîne de caractères (string)
- Une valeur de type double
Démontrez vos templates en :
- Créant un
Storage<int>avec le premier entier, en affichant :Int storage: <value> - Le mettant à jour avec le deuxième entier en utilisant
setValue(), puis en affichant :Updated: <value> - Créant un
Storage<std::string>avec la chaîne de caractères saisie, en affichant :String storage: <value> - Vérifiant si le stockage de la chaîne est vide et en affichant :
Is empty: <true/false>(afficheztrueoufalse) - Créant une
Pair<std::string, double>avec les valeurs string et double, puis en appelantdisplay() - Créant une
Pair<int, int>avec les deux entrées entières, puis en appelantdisplay()
Par exemple, avec les entrées 42, 100, Hello, et 3.14 :
Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)Remarquez comment le même template Storage fonctionne parfaitement avec des entiers et des chaînes de caractères, et comment Pair peut combiner différents types ensemble. Le compilateur génère des définitions de classe distinctes pour chaque combinaison de types que vous utilisez. N'oubliez pas d'utiliser la syntaxe template <typename T> pour les templates à type unique et template <typename T, typename U> pour les templates avec plusieurs paramètres de type.
Aide-mémoire
Les modèles de classes vous permettent de créer des classes indépendantes du type. Le compilateur génère des versions spécifiques pour chaque type que vous utilisez.
Modèle de classe de base
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};Lors de la création d'objets à partir de modèles de classes, vous devez spécifier explicitement le type à l'aide de chevrons :
Box<int> intBox(42);
Box<std::string> strBox("Hello");Paramètres de type multiples
Les modèles de classes peuvent avoir plusieurs paramètres de type :
template <typename T, typename U>
class Pair {
T first;
U second;
public:
Pair(T f, U s) : first(f), second(s) {}
};Paramètres non-types
Les modèles peuvent également accepter des paramètres non-types comme des entiers :
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Tableau de 5 doublesDéfinition des fonctions membres en dehors de la classe
Lors de la définition de fonctions membres en dehors de la classe, répétez la déclaration du modèle :
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Essayez vous-même
#include <iostream>
#include <string>
#include "Storage.h"
using namespace std;
int main() {
// Lire les entrées
int intValue1;
int intValue2;
string strValue;
double doubleValue;
cin >> intValue1;
cin >> intValue2;
cin >> strValue;
cin >> doubleValue;
// TODO: Créer Storage<int> avec le premier entier et afficher : Int storage: <value>
// TODO: Mettre à jour avec le deuxième entier en utilisant setValue(), afficher : Updated: <value>
// TODO: Créer Storage<std::string> avec la chaîne en entrée, afficher : String storage: <value>
// TODO: Vérifier si le stockage de la chaîne est vide, afficher : Is empty: true/false
// TODO: Créer Pair<std::string, double> avec la chaîne et le double, appeler display()
// TODO: Créer Pair<int, int> avec les deux entiers, appeler display()
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique