Enum Classes & Typage fort
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 80 sur 104.
Le C++11 a introduit les enum classes (également appelées énumérations à portée limitée) pour remédier aux lacunes des énumérations traditionnelles de style C. Elles offrent une sécurité de type plus forte et empêchent l'utilisation abusive accidentelle des valeurs d'énumération.
Les enums traditionnels posent des problèmes : leurs valeurs s'échappent dans la portée environnante et se convertissent implicitement en entiers, ce qui entraîne des bugs subtils :
// Enum traditionnel - problématique
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Erreur ! Red et Green sont déjà définis
int x = Red; // Se convertit implicitement en int (0)
if (Red == 0) { } // Compile - comparaison d'un enum avec un intLes classes d'énumération (enum classes) résolvent ces problèmes en limitant la portée des valeurs et en empêchant les conversions implicites :
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; // OK - pas de conflit
Color c = Color::Red; // Doit utiliser l'opérateur de portée
// int x = c; // Erreur ! Pas de conversion implicite
int x = static_cast<int>(c); // Conversion explicite requise
// if (c == 0) { } // Erreur ! Impossible de comparer à un int
if (c == Color::Red) { } // OK - comparaison de types identiquesVous pouvez également spécifier le type sous-jacent pour le contrôle de la mémoire :
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Les classes Enum s'intègrent bien aux conceptions POO, rendant le code plus lisible et permettant de détecter les erreurs de type au moment de la compilation plutôt qu'à l'exécution.
Défi
FacileConstruisons un système de priorité de tâches qui utilise des classes enum pour représenter différents niveaux de priorité et statuts de tâches. Cela démontre comment les classes enum assurent la sécurité des types et préviennent les types de bogues qui surviennent avec les enums traditionnels.
Vous organiserez votre code sur trois fichiers :
TaskTypes.h: Définissez vos classes enum pour le système de tâches.Créez une classe enum
Priorityavec les valeurs :Low,Medium,High, etCritical. Spécifiezintcomme type sous-jacent, avec les valeurs 1, 2, 3, et 4 respectivement.Créez une classe enum
Statusavec les valeurs :Pending,InProgress, etCompleted.Fournissez deux fonctions d'aide :
priorityToString(Priority p)— retourne la priorité sous forme de chaîne de caractères ("Low", "Medium", "High", ou "Critical")statusToString(Status s)— retourne le statut sous forme de chaîne de caractères ("Pending", "InProgress", ou "Completed")
Task.h: Définissez une classeTaskqui utilise vos classes enum.Votre
Taskdoit stocker un nom (string), unePriority, et unStatus. Le constructeur doit prendre un nom et une priorité, le statut étant par défautStatus::Pending.Implémentez ces méthodes :
setStatus(Status s)— met à jour le statut de la tâchegetPriorityValue()— retourne la valeur entière sous-jacente de la priorité en utilisantstatic_castdisplay()— affiche la tâche au format :[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: Lisez trois entrées :- Nom de la tâche (string)
- Niveau de priorité sous forme d'entier (1=Low, 2=Medium, 3=High, 4=Critical)
- Statut sous forme d'entier (0=Pending, 1=InProgress, 2=Completed)
Créez une
Taskavec le nom et la priorité donnés (convertissez l'entier en la valeur enumPriorityappropriée en utilisantstatic_cast). Ensuite, définissez son statut en fonction de la troisième entrée. Enfin, appelezdisplay()pour afficher les détails de la tâche.
Par exemple, avec les entrées Fix bug, 3, et 1 :
Fix bug - Priority: High (Value: 3) - Status: InProgressAvec les entrées Write docs, 1, et 2 :
Write docs - Priority: Low (Value: 1) - Status: CompletedAvec les entrées Deploy app, 4, et 0 :
Deploy app - Priority: Critical (Value: 4) - Status: PendingRemarquez comment les classes enum maintiennent les valeurs de priorité et de statut complètement séparées — vous ne pouvez pas comparer accidentellement une Priority à un Status ou assigner l'un à l'autre. Le static_cast explicite requis pour convertir entre les entiers et les valeurs enum rend l'intention du code claire et prévient les bogues subtils.
Aide-mémoire
Le C++11 a introduit les enum classes (énumérations à portée limitée) pour offrir une sécurité de type plus forte que les énumérations traditionnelles de style C.
Les énumérations traditionnelles posent des problèmes de fuite de portée et de conversions implicites :
// Énumération traditionnelle - problématique
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Erreur ! Red et Green sont déjà définis
int x = Red; // Conversion implicite en int
if (Red == 0) { } // Compile - comparaison d'une énumération avec un intLes enum classes résolvent ces problèmes en exigeant la résolution de portée et en empêchant les conversions implicites :
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; // OK - pas de conflit
Color c = Color::Red; // Doit utiliser l'opérateur de portée
// int x = c; // Erreur ! Pas de conversion implicite
int x = static_cast<int>(c); // Conversion explicite requise
// if (c == 0) { } // Erreur ! Impossible de comparer avec un int
if (c == Color::Red) { } // OK - comparaison de types identiquesVous pouvez spécifier le type sous-jacent pour le contrôle de la mémoire :
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Pour convertir entre les enum classes et les entiers, utilisez un transtypage (cast) explicite :
// De l'entier vers l'enum class
Priority p = static_cast<Priority>(3);
// De l'enum class vers l'entier
int value = static_cast<int>(p);Essayez vous-même
#include <iostream>
#include <string>
#include "Task.h"
using namespace std;
int main() {
// Lire les entrées
string taskName;
int priorityInt;
int statusInt;
getline(cin, taskName);
cin >> priorityInt;
cin >> statusInt;
// TODO: Créer une Task avec le nom et la priorité donnés
// Utiliser static_cast pour convertir priorityInt en énumération Priority
// TODO: Définir le statut de la tâche en fonction de statusInt
// Utiliser static_cast pour convertir statusInt en énumération Status
// TODO: Appeler display() pour afficher les détails de la tâche
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