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 bereichsbezogene Enumerationen genannt) ein, um die Schwächen traditioneller C-ähnlicher Enums zu beheben. Sie bieten eine stärkere Typsicherheit und verhindern die versehentliche missbräuchliche Verwendung von Enumerationswerten.
Traditionelle Enums haben Probleme: Ihre Werte gelangen in den umgebenden Gültigkeitsbereich und werden implizit in Integer konvertiert, was zu schwer erkennbaren Bugs führt:
// Traditionelles enum - problematisch
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Fehler! Red und Green bereits definiert
int x = Red; // Konvertiert implizit zu int (0)
if (Red == 0) { } // Kompiliert - Vergleich von enum mit intEnum-Klassen lösen diese Probleme, indem sie Werte in einen Gültigkeitsbereich 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 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 gleicher TypenDu kannst auch den zugrunde liegenden Typ zur Speichersteuerung angeben:
enum class Status : uint8_t {
Inactive = 0,
Active = 1,
Pending = 2
};Enum-Klassen lassen sich gut in OOP-Designs integrieren, machen den Code lesbarer und erkennen Typfehler bereits zur Kompilierzeit statt erst zur Laufzeit.
Aufgabe
EinfachWir erstellen ein Aufgabenprioritätssystem, das Enum-Klassen verwendet, um verschiedene Prioritätsstufen und Aufgabenstatus darzustellen. Dies veranschaulicht, wie Enum-Klassen Typsicherheit bieten und die Arten von Fehlern verhindern, die bei herkömmlichen Enums auftreten.
Du organisierst deinen Code über drei Dateien:
TaskTypes.h: Definiere deine Enum-Klassen für das Aufgabensystem.Erstelle eine
Priority-Enum-Klasse mit den Werten:Low,Medium,HighundCritical. Legeintals zugrunde liegenden Typ fest, mit den Werten 1, 2, 3 beziehungsweise 4.Erstelle eine
Status-Enum-Klasse mit den Werten:Pending,InProgressundCompleted.Stelle zwei Hilfsfunktionen bereit:
priorityToString(Priority p): gibt die Priorität als Zeichenkette zurück ("Low", "Medium", "High" oder "Critical")statusToString(Status s): gibt den Status als Zeichenkette zurück ("Pending", "InProgress" oder "Completed")
Task.h: Definiere eineTask-Klasse, die deine Enum-Klassen verwendet.Deine
Tasksoll einen Namen (Zeichenkette), einePriorityund einenStatusspeichern. Der Konstruktor soll einen Namen und eine Priorität übernehmen, wobei der Status standardmäßig aufStatus::Pendinggesetzt wird.Implementiere diese Methoden:
setStatus(Status s): aktualisiert den Status der AufgabegetPriorityValue(): gibt den zugrunde liegenden Ganzzahlwert der Priorität mithilfe vonstatic_castzurückdisplay(): gibt die Aufgabe im folgenden Format aus:[name] - Priority: [priority] (Value: [value]) - Status: [status]
main.cpp: Lies drei Eingaben ein:- Aufgabenname (Zeichenkette)
- Prioritätsstufe als Ganzzahl (1=Low, 2=Medium, 3=High, 4=Critical)
- Status als Ganzzahl (0=Pending, 1=InProgress, 2=Completed)
Erstelle eine
Taskmit dem angegebenen Namen und der angegebenen Priorität (wandle die Ganzzahl mithilfe vonPriorityin den entsprechendenstatic_cast-Enum-Wert um). Setze anschließend den Status basierend auf der dritten Eingabe. Rufe schließlichdisplay()auf, um die Aufgabendetails anzuzeigen.
Bei den Eingaben Fix bug, 3 und 1 ergibt sich beispielsweise:
Fix bug - Priority: High (Value: 3) - Status: InProgressBei den Eingaben Write docs, 1 und 2 ergibt sich:
Write docs - Priority: Low (Value: 1) - Status: CompletedBei den Eingaben Deploy app, 4 und 0 ergibt sich:
Deploy app - Priority: Critical (Value: 4) - Status: PendingBeachte, wie die Enum-Klassen die Werte für Priorität und Status vollständig getrennt halten. Du kannst nicht versehentlich eine Priority mit einem Status vergleichen oder das eine dem anderen zuweisen. Das explizite static_cast, das für die Umwandlung zwischen Ganzzahlen und Enum-Werten erforderlich ist, macht die Absicht des Codes deutlich und verhindert subtile Fehler.
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 einen Task mit dem gegebenen 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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator11Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixins mittels CRTPPimpl-IdiomTyplöschungEnum-Klassen & starke TypisierungAusnahmebehandlung in OOPBenutzerdefinierte Ausnahmehierarchien3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler