C++ Tutorial

enum Datentyp

Der enumerated Datentyp (im Folgenden vereinfacht enum genannt) ist ein anwenderdefinierter Datentyp, der eingesetzt wird, um mehrere logisch zusammengehörige Symbole (die Enumeratoren) zu definieren. Es gibt zwei Arten von enums, den scoped enum und den unscoped enum.

scoped enum

Die enum-Anweisung für ein scoped enum hat folgende Syntax:

enum class ETYP [:DTYP] {E1[=VAL1],E2[=VAL2],... }[EOBJ1,...];

Nach den Schlüsselwörtern enum class folgt ein eindeutiger Name ETYP, der den anwenderdefinierten enum-Datentyp benennt.

Im Anschluss daran folgen optional ein Doppelpunkt und der dem enum zugrunde liegende Datentyp DTYP. Dieser kann ein Zeichen- oder Integer-Datentyp sein, jedoch muss in der Lage sein die internen Werte für die Enumeratoren abzubilden. Standardmäßig wird der 'kleinste' Integer-Datentyp verwendet, in dem die Werte der Enumeratoren dargestellt werden können.

Innerhalb der geschweiften Klammern folgen die symbolischen Namen der Enumeratoren Ex.

Nach der schließenden geschweiften Klammer können optional Objekte des enum-Datentyps definiert werden.

1: // enum-Datentyp definieren und gleichzeitig
2: // ein enum-Objekt definieren
3: enum class Colors
4: {
5:    RED, YELLOW, GREEN, BLUE
6: } myColors;
7: // weitere enum-Objekte definieren
8: Colors color1{Colors::RED}, color2;

Im Beispiel wird der enum-Datentyp Colors, mit den Enumeratoren RED, YELLOW, GREEN und BLUE sowie das enum-Objekt myColors definiert. Im Anschluss daran werden zwei weitere Objekte color1 und color2 vom gleichen enum-Datentyp definiert, wobei color1 mit dem Enumerator RED initialisiert wird.

Einem enum-Objekt können nur die innerhalb des enum-Blocks definierten Enumeratoren zugewiesen werden. Dabei ist der Enumerator voll zu qualifizieren, d.h vor dem Enumerator ist der enum-Datentyp gefolgt von zwei Doppelpunkten anzugeben. Der Grund hierfür ist, dass unterschiedliche enum-Datentypen gleichnamige Enumeratoren enthalten können, ohne dass dies zu einer Doppeldeutigkeit führt.

Das nachfolgende Beispiel definiert eine enum-Variable s1, die die Zustände eines Schalters abbildet.

1: // enum Definition
2: enum class Switch {OFF, LOW, HIGH};
3: // Definition eines Schalters
4: Switch s1;
5:
6: int main()
7: {
8:    s1 = Switch::LOW;
9: }

Werden in einem Block {...} häufig Zugriffe auf die Enumeratoren eines scoped enum benötigt, können mithilfe der Anweisung

using enum ETYP;

die Enumeratoren des enum-Typs ETYP eingeblendet werden. In diesem Fall können die Enumeratoren ohne volle Qualifizierung verwendet werden.

1: // enum fuer Schalterzustände
2: enum class Switch {OFF, LOW, HIGH} s1;
3: ...
4: {
5:    // enum Schalter einblenden
6:    using enum Switch;
7:    s1 = LOW;
8: }

unscoped enum

Die zweite Form der enum-Anweisung gleicht bis auf das Schlüsselwort class der ersten Form:

enum [ETYP] [:DTYP] {E1[=VAL1],E2[=VAL2],... }[EOBJ1,...];

unscoped enums sind nicht typsicher, d.h., besitzen unterschiedliche unscoped enums gleichnamige Enumeratoren, führt dies zu einem Fehler.

Der 'Vorteil' dafür ist, dass der Zugriff auf die Enumeratoren durch alleinig Angabe des Enumerators erfolgt, d.h es ist keine volle Qualifizierung notwendig.

1: // unscoped enum definieren
2: enum Style {BOLD, ITALIC, UNDERLINE};
3:
4: // enum-Variable mit Initialsierung
5: Style myStyle{UNDERLINE};
6:
7: int main()
8: {
9:     // Zuweisung an enum-Variable
10:    myStyle = BOLD;
11: }

Werte der Enumeratoren

Enumeratoren werden standardmäßig fortlaufend inkrementiert, wobei der erste Enumerator den Wert 0 besitzt. Soll einem Enumerator explizit ein Wert zugewiesen werden, folgen nach dem Enumerator der Zuweisungsoperator und der Wert.

enum class Schalter {OFF=1, LOW, HIGH=LOW+10};

Alle nach einer Zuweisung folgenden Enumeratoren werden, wenn ihnen nicht erneut ein Wert zugewiesen wird, wieder fortlaufend um eins erhöht. Ein in der Liste definierter Enumerator kann zur Berechnung des Wertes eines nachfolgenden Enumerators verwendet werden. Im Beispiel oben besitzt der Enumerator OFF den Wert 1, LOW den Wert 2 und HIGH den Wert 12.

Mithilfe der in der Header-Datei <utility> definierten Bibliotheksfunktion std::to_underlying(e) kann der Enumerator e eines enums in seinen Integerwert konvertiert werden, um damit z.B. Rechenoperationen durchzuführen. Dabei ist zu beachten, dass das Ergebnis der Rechenoperation aber ebenfalls ein Integerwert ist. So gibt das nachfolgende Beispiel als Ergebnis den Wert 3 aus.

1: #include <iostream>
2: #include <utility>
3:
4: // scoped enum definieren
5: enum class Style {BOLD=1, ITALIC=2, UNDERLINE=4};
6:
7: // enum-Variable mit Initialsierung
8: Style myStyle;
9:
10: int main()
11: {
12:    auto var = std::to_underlying(Style::BOLD) |
13:               std::to_underlying(Style::ITALIC);
14:    std::cout << var;
15: }

Fehlerfallen

Mit einem enum-Objekt können keine Rechenoperationen durchgeführt werden, d.h., die in der for-Schleife angegebene Anweisung s1++ führt zu einem Fehler.

1: // Definition
2: enum class Switch {OFF, LOW, HIGH} s1;
3: ...
4: // enum in einer while Schleife ist ok
5: s1=Switch::OFF;
6: while (s1 != Switch::HIGH)
7: {
8:    ...
9: }
10: // Aber das geht nicht!
11: for (s1=Switch::OFF; s1!=Switch::HIGH; s1++)
12:    ...

Eine weitere Fehlerfalle lauert bei der Übergabe eines enums an eine Funktion. Im nachfolgenden Beispiel wird der enum-Datentyp Style zur Definition von verschiedenen Stilen definiert. An die Funktion Func() soll ein Datum dieses enum-Datentyps übergeben werden.

1: // Enum-Definition
2: enum class Style {STYLE1, STYLE2, STYLE3};
3:
4: // Funktionsdeklaration
5: void Func(Style newStyle);
6:
7:  // So geht's
8:  Func(Style::STYLE2);
9:  // Aber das erzeugt einen Fehler
10: Func(Style::STYLE1 | Style::STYLE2);

Wird die Funktion mit einem Enumerator aufgerufen, ist alles in Ordnung (erster Funktionsaufruf). Doch Vorsicht! Werden mehrere Enumeratoren beim Aufruf der Funktion z.B. verodert, meldet der Compiler beim Übersetzen einen Fehler (zweiter Funktionsaufruf). Der Grund dafür ist prinzipiell der gleiche wie im vorherigen Fall: mit den Enumeratoren können standardmäßig keine Rechenoperationen durchgeführt werden.


Copyright 2024 © Wolfgang Schröder
E-Mail mit Fragen oder Kommentaren zu dieser Website an: info@cpp-tutor.de
Impressum & Datenschutz