Classes enum et typage fort
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 80 sur 104.
C++11 a introduit les classes enum (également appelées énumérations à portée contrôlée) pour remédier aux limitations des énumérations traditionnelles de style C. Elles offrent une sécurité de typage renforcée et empêchent l’utilisation accidentelle et incorrecte des valeurs d’énumération.
Les énumérations traditionnelles posent des problèmes : leurs valeurs se propagent 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 déjà définis
int x = Red; // Convertit implicitement en int (0)
if (Red == 0) { } // Compile - comparaison d'enum à intLes classes d’énumération 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 à int
if (c == Color::Red) { } // OK - comparaison des mêmes typesVous pouvez également spécifier le type sous-jacent pour contrôler la mémoire :
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Les classes enum s’intègrent bien aux conceptions orientées objet, rendant le code plus lisible et détectant les erreurs de type lors de la compilation plutôt qu’à l’exécution.
Défi
FacileConstruisons un système de priorité des tâches qui utilise des classes d’énumération pour représenter différents niveaux de priorité et statuts de tâche. Cela montre comment les classes d’énumération assurent la sécurité des types et empêchent les types de bogues qui se produisent avec les énumérations traditionnelles.
Vous organiserez votre code dans trois fichiers :
TaskTypes.h: définissez vos classes d’énumération pour le système de tâches.Créez une classe d’énumération
Priorityavec les valeurs suivantes :Low,Medium,HighetCritical. Spécifiezintcomme type sous-jacent, avec les valeurs 1, 2, 3 et 4 respectivement.Créez une classe d’énumération
Statusavec les valeurs suivantes :Pending,InProgressetCompleted.Fournissez deux fonctions d’aide :
priorityToString(Priority p): renvoie la priorité sous forme de chaîne ("Low", "Medium", "High" ou "Critical")statusToString(Status s): renvoie le statut sous forme de chaîne ("Pending", "InProgress" ou "Completed")
Task.h: définissez une classeTaskqui utilise vos classes d’énumération.Votre
Taskdoit stocker un nom (chaîne), unePriorityet unStatus. Le constructeur doit accepter un nom et une priorité, le statut prenant par défaut la valeurStatus::Pending.Implémentez ces méthodes :
setStatus(Status s): met à jour le statut de la tâchegetPriorityValue(): renvoie la valeur entière sous-jacente de la priorité à l’aide destatic_castdisplay(): affiche la tâche au format suivant :[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: lisez trois entrées :- Nom de la tâche (chaîne)
- 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é fournis (convertissez l’entier en valeur d’énumérationPriorityappropriée à l’aide destatic_cast). Définissez ensuite 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 d’énumération maintiennent les valeurs de priorité et de statut complètement séparées. Vous ne pouvez pas comparer accidentellement une Priority à un Status, ni affecter l’une à l’autre. Le static_cast explicite requis pour effectuer une conversion entre des entiers et des valeurs d’énumération rend l’intention du code claire et empêche les bogues subtils.
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 un Task avec le nom et la priorité donnés
// Utiliser static_cast pour convertir priorityInt en enum Priority
// TODO: Définir le statut de la tâche en fonction de statusInt
// Utiliser static_cast pour convertir statusInt en enum 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 externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents 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 de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition ou héritageMixins via CRTPIdiom PimplEffacement de typeClasses enum et typage fortGestion des exceptions en POOHiérarchies d’exceptions personnalisées3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des 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 des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne