Menu
Coddy logo textTech

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 int

Enum-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 Typen

Sie 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.

challenge icon

Aufgabe

Einfach

Lassen 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 Priority Enum-Klasse mit den Werten: Low, Medium, High und Critical. Geben Sie int als zugrunde liegenden Typ an, mit den Werten 1, 2, 3 bzw. 4.

    Erstellen Sie eine Status Enum-Klasse mit den Werten: Pending, InProgress und Completed.

    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 eine Task Klasse, die Ihre Enum-Klassen verwendet.

    Ihre Task sollte einen Namen (String), eine Priority und einen Status speichern. Der Konstruktor sollte einen Namen und eine Priorität entgegennehmen, wobei der Status standardmäßig auf Status::Pending gesetzt wird.

    Implementieren Sie diese Methoden:

    • setStatus(Status s) — aktualisiert den Status der Aufgabe
    • getPriorityValue() — gibt den zugrunde liegenden ganzzahligen Wert der Priorität unter Verwendung von static_cast zurück
    • display() — gibt die Aufgabe im Format: [name] - Priority: [priority] (Value: [value]) - Status: [status] aus
  • main.cpp: Lesen Sie drei Eingaben ein:
    1. Aufgabenname (String)
    2. Prioritätsstufe als Ganzzahl (1=Low, 2=Medium, 3=High, 4=Critical)
    3. Status als Ganzzahl (0=Pending, 1=InProgress, 2=Completed)

    Erstellen Sie eine Task mit dem angegebenen Namen und der Priorität (konvertieren Sie die Ganzzahl mit static_cast in den entsprechenden Priority Enum-Wert). Setzen Sie dann den Status basierend auf der dritten Eingabe. Rufen Sie schließlich display() auf, um die Aufgabendetails anzuzeigen.

Zum Beispiel mit den Eingaben Fix bug, 3 und 1:

Fix bug - Priority: High (Value: 3) - Status: InProgress

Mit den Eingaben Write docs, 1 und 2:

Write docs - Priority: Low (Value: 1) - Status: Completed

Mit den Eingaben Deploy app, 4 und 0:

Deploy app - Priority: Critical (Value: 4) - Status: Pending

Beachten 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 int

Enum-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 Typen

Sie 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;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung