Enum-Klassen & starke Typisierung
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 80 von 104.
C++11 führte enum classes (auch Scoped Enumerations genannt) ein, um die Unzulänglichkeiten traditioneller Enums im C-Stil zu beheben. Sie bieten eine stärkere Typsicherheit und verhindern den versehentlichen Missbrauch von Aufzählungswerten.
Herkömmliche Enums haben Probleme: Ihre Werte gelangen in den umgebenden Gültigkeitsbereich und werden implizit in Ganzzahlen umgewandelt, was zu subtilen Fehlern führt:
// Herkömmliches Enum - problematisch
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Fehler! Red und Green sind bereits definiert
int x = Red; // Wird implizit in int (0) umgewandelt
if (Red == 0) { } // Kompiliert - Vergleich von Enum mit intEnum-Klassen lösen diese Probleme, indem sie Werte in einen Gültigkeitsbereich (Scope) einschließen und implizite Konvertierungen verhindern:
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; // OK - kein Konflikt
Color c = Color::Red; // Muss den Scope-Operator verwenden
// int x = c; // Fehler! Keine implizite Konvertierung
int x = static_cast<int>(c); // Explizite Konvertierung erforderlich
// if (c == 0) { } // Fehler! Kann nicht mit int verglichen werden
if (c == Color::Red) { } // OK - Vergleich derselben TypenSie können auch den zugrunde liegenden Typ für die Speichersteuerung angeben:
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Enum-Klassen integrieren sich gut in OOP-Designs, wodurch der Code lesbarer wird und Typfehler bereits zur Kompilierzeit statt erst zur Laufzeit abgefangen werden.
Aufgabe
EinfachLassen Sie uns ein Aufgabenprioritätssystem erstellen, das Enum-Klassen verwendet, um verschiedene Prioritätsstufen und Aufgabenstati darzustellen. Dies zeigt, wie Enum-Klassen Typsicherheit bieten und die Arten von Fehlern verhindern, die bei herkömmlichen Enums auftreten.
Sie werden Ihren Code über drei Dateien organisieren:
TaskTypes.h: Definieren Sie Ihre Enum-Klassen für das Aufgabensystem.Erstellen Sie eine
PriorityEnum-Klasse mit den Werten:Low,Medium,HighundCritical. Geben Sieintals zugrunde liegenden Typ an, mit den Werten 1, 2, 3 bzw. 4.Erstellen Sie eine
StatusEnum-Klasse mit den Werten:Pending,InProgressundCompleted.Stellen Sie zwei Hilfsfunktionen bereit:
priorityToString(Priority p)— gibt die Priorität als String zurück ("Low", "Medium", "High" oder "Critical")statusToString(Status s)— gibt den Status als String zurück ("Pending", "InProgress" oder "Completed")
Task.h: Definieren Sie eineTaskKlasse, die Ihre Enum-Klassen verwendet.Ihre
Tasksollte einen Namen (String), einePriorityund einenStatusspeichern. Der Konstruktor sollte einen Namen und eine Priorität entgegennehmen, wobei der Status standardmäßig aufStatus::Pendinggesetzt wird.Implementieren Sie diese Methoden:
setStatus(Status s)— aktualisiert den Status der AufgabegetPriorityValue()— gibt den zugrunde liegenden ganzzahligen Wert der Priorität unter Verwendung vonstatic_castzurückdisplay()— gibt die Aufgabe im Format:[name] - Priority: [priority] (Value: [value]) - Status: [status]aus
main.cpp: Lesen Sie drei Eingaben ein:- Aufgabenname (String)
- Prioritätsstufe als Ganzzahl (1=Low, 2=Medium, 3=High, 4=Critical)
- Status als Ganzzahl (0=Pending, 1=InProgress, 2=Completed)
Erstellen Sie eine
Taskmit dem angegebenen Namen und der Priorität (konvertieren Sie die Ganzzahl mitstatic_castin den entsprechendenPriorityEnum-Wert). Setzen Sie dann den Status basierend auf der dritten Eingabe. Rufen Sie schließlichdisplay()auf, um die Aufgabendetails anzuzeigen.
Zum Beispiel mit den Eingaben Fix bug, 3 und 1:
Fix bug - Priority: High (Value: 3) - Status: InProgressMit den Eingaben Write docs, 1 und 2:
Write docs - Priority: Low (Value: 1) - Status: CompletedMit den Eingaben Deploy app, 4 und 0:
Deploy app - Priority: Critical (Value: 4) - Status: PendingBeachten Sie, wie die Enum-Klassen Prioritäts- und Statuswerte vollständig getrennt halten – Sie können nicht versehentlich eine Priority mit einem Status vergleichen oder einen dem anderen zuweisen. Das explizite static_cast, das für die Konvertierung zwischen Ganzzahlen und Enum-Werten erforderlich ist, macht die Absicht des Codes klar und verhindert subtile Fehler.
Spickzettel
C++11 führte enum classes (bereichsbezogene Enumerationen) ein, um eine stärkere Typsicherheit als herkömmliche Enums im C-Stil zu bieten.
Herkömmliche Enums haben Probleme mit Scope-Leakage (Namensraum-Konflikten) und impliziten Konvertierungen:
// Herkömmliches Enum - problematisch
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Fehler! Red und Green sind bereits definiert
int x = Red; // Implizite Konvertierung in int
if (Red == 0) { } // Kompiliert - Vergleich von Enum mit intEnum-Klassen lösen diese Probleme, indem sie eine Bereichsauflösung (Scope Resolution) erfordern und implizite Konvertierungen verhindern:
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; // OK - kein Konflikt
Color c = Color::Red; // Bereichsoperator muss verwendet werden
// int x = c; // Fehler! Keine implizite Konvertierung
int x = static_cast<int>(c); // Explizite Konvertierung erforderlich
// if (c == 0) { } // Fehler! Kann nicht mit int verglichen werden
if (c == Color::Red) { } // OK - Vergleich gleicher TypenSie können den zugrunde liegenden Typ für die Speicherkontrolle angeben:
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Um zwischen Enum-Klassen und Ganzzahlen zu konvertieren, verwenden Sie explizites Casting:
// Ganzzahl zu Enum-Klasse
Priority p = static_cast<Priority>(3);
// Enum-Klasse zu Ganzzahl
int value = static_cast<int>(p);Probier es selbst
#include <iostream>
#include <string>
#include "Task.h"
using namespace std;
int main() {
// Eingaben lesen
string taskName;
int priorityInt;
int statusInt;
getline(cin, taskName);
cin >> priorityInt;
cin >> statusInt;
// TODO: Erstelle ein Task mit dem angegebenen Namen und der Priorität
// Verwende static_cast, um priorityInt in das Priority-Enum umzuwandeln
// TODO: Setze den Status des Tasks basierend auf statusInt
// Verwende static_cast, um statusInt in das Status-Enum umzuwandeln
// TODO: Rufe display() auf, um die Task-Details anzuzeigen
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner11Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixins via CRTPPimpl-IdiomType ErasureEnum-Klassen & starke TypisierungException-Handling in OOPEigene Exception-Hierarchien3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container