Warum enum class Ihr neuer bester Freund in C++ ist?
Jeder, der angefangen hat, C++ zu lernen oder nach einer Pause zurückgekehrt ist, steht früher oder später vor dem Dilemma: den guten alten enum oder seinen moderneren Verwandten enum class verwenden? Auf Stack Overflow hat die Frage „Why is enum class considered safer to use than plain enum?“ Tausende von Aufrufen gesammelt – und das ist kein Zufall. Der Wechsel zu scoped enum (so heißt enum class offiziell) ist nicht nur eine Modeerscheinung, sondern ein echter Schritt zu zuverlässigerem und lesbarerem Code.
Lassen Sie uns herausfinden, worin genau die Gefahr des alten enum liegt und wie enum class hilft, eine ganze Klasse von Fehlern zu vermeiden. Wenn Sie in C++ programmieren und immer noch enum ohne Gültigkeitsbereich verwenden – dieser Artikel ist für Sie.
Die Top-3-Fehler des alten enum (und wie enum class sie behebt)
1️⃣ Verschmutzung des globalen Gültigkeitsbereichs
Das häufigste Problem des alten enum ist, dass seine Konstanten in den umgebenden Namensraum „herausfallen“. Stellen Sie sich vor, Sie haben zwei Aufzählungen:
// Schlecht: alter enumenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // Kompilierungsfehler! Blue ist bereits definiertDie Konstante Blue aus Color ist bereits belegt, und Sie können denselben Namen nicht in Feeling verwenden. In einem großen Projekt mit Hunderten von Dateien führt dies zu echtem Kopfzerbrechen. Sie müssen lange Namen wie Color_Blue oder COLOR_BLUE erfinden, was den Code verschandelt.
Lösung mit enum class:
// Gut: enum class (scoped enum)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // OK! Kein KonfliktJetzt lebt Blue innerhalb seiner Klasse. Um darauf zuzugreifen, müssen Sie Color::Blue oder Feeling::Blue schreiben. Dies löst nicht nur das Problem der Kollisionen, sondern macht den Code auch selbstdokumentierend – man sieht sofort, zu welchem Typ der Wert gehört.
2️⃣ Implizite Konvertierung zu int (und zurück)
Der alte enum wird implizit in ganzzahlige Typen konvertiert. Auf den ersten Blick ist das praktisch, aber in der Praxis ist es eine Quelle schwer zu findender Fehler.
// Gefährlicher alter enumenum WarningLevel { Low, Medium, High };
void setVolume(int level) { if (level > 10) level = 10; // ...}
setVolume(High); // Funktioniert! Aber High = 2, nicht die Lautstärke// Sie haben versehentlich die Alarmstufe statt der Lautstärke übergeben – der Compiler schweigtDer Compiler schluckt so etwas ohne eine einzige Warnung. Und wenn Sie die Reihenfolge der Argumente in einer Funktion vertauschen – der Fehler geht in die Laufzeit.
Wie schützt enum class:
enum class WarningLevel { Low, Medium, High };
void setVolume(int level);
setVolume(WarningLevel::High); // Kompilierungsfehler!// Keine implizite Konvertierung von enum class zu intDer Compiler zwingt Sie, den Wert explizit zu konvertieren, wenn dies wirklich nötig ist. Und das bedeutet, dass Sie sich jeder Konvertierung bewusst sind und sich nicht auf „wird schon gut gehen“ verlassen.
// Wenn es unbedingt nötig ist – machen wir es explizitsetVolume(static_cast<int>(WarningLevel::High)); // Explizit und klar3️⃣ Undefinierter Basistyp
Der alte enum garantiert nicht, welcher ganzzahlige Typ unter der Haube verwendet wird. Der Compiler wählt ihn selbst (normalerweise int), und dies kann zu Überraschungen bei der Übergabe zwischen verschiedenen Compilern oder Plattformen führen.
// Die Größe kann unterschiedlich sein!enum Small { A, B, C }; // Kann int sein, kann char seinenum Big { X = 100000, Y, Z }; // Kann immer noch int seinMit enum class können Sie den Basistyp explizit angeben, was Ihnen die volle Kontrolle gibt