C++-Programmierung/ Weitere Grundelemente/ Aufzählungen


Eine Aufzählung oder Enumeration ist in C++ ein Typ, der eine Menge durch den Benutzer definierte Werte abbilden kann. Diese Konstanten werden als Enumeratoren bezeichnet. Im folgenden Beispiel wird eine Enumeration color definiert, welche mehrere Farbnamen als Enumeratoren enthält.

enum color{
    red, green, blue, yellow, black, gray, white
};

Diese Typdefinition kann nun zum Anlegen einer Variablen verwendet werden:

color meineFarbe = yellow; // genauso wie:  int i = 5;

Die Enumeration ist eng mit den ganzzahligen Datentypen verwandt, da die definierten Werte Ganzzahlkonstanten entsprechen:

enum color{
    red,        // Index 0
    green,      // Index 1
    blue,       // Index 2
    yellow = 9, // Index 9
    black,      // Index 10
    gray = -1,  // Index -1
    white,      // Index 0
};

Die Werte hinter den Konstanten werden als Indizes der Enumeration bezeichnet. Der Index kann explizit angegeben, oder vom Compiler implizit automatisch vergeben werden. Bei einer expliziten Angabe kann ein beliebiger positiver oder negativer konstanter Ganzzahlwert vergeben werden. Ist kein Index angegeben, wird der Compiler von der letzten expliziten Indexangabe aus die Werte hochzählen, beziehungsweise im Falle des ersten Enumerators den Wert 0 vergeben. Im Beispiel wurden die Indizes teils explizit, teils implizit vergeben, wobei der Indexwert in jedem Fall als Kommentar angegeben ist.

Wie Sie leicht erkennen werden, haben red und white den gleichen Index, was bedeutet, dass die beiden Enumeratoren gleichwertig sind. Auch ist es offensichtlich möglich die Indizes nicht durchgehend zu vergeben, beispielsweise sind die Werte zwischen 3 und 8 nicht verwendet. Das letzte Komma hinter white ist optional und hat den Vorteil, dass die Liste leicht erweitert werden kann. In größeren Bibliotheken kann es vorkommen, dass die Liste mittels Makros manipuliert wird, wofür das optionale Komma nützlich ist. Im Normalfall können Sie es auch weglassen.

Hinweis

Mit Enumeratoren als Zahlenwerten zu hantieren, führt leicht zu Problemen, davon ist abzuraten. Im Beispiel sind die Werte 3..8 nicht definiert; werden sie dennoch verwendet, so entsprechen sie keinem enum-Enumerator, und eine Abfrage

switch( ampelFarbe ) {
    case red:
    case green:
    case yellow:
        std::cout << "normale Ampelfarbe" << std::endl ;
        break;
    case black:
        std::cout << "Ampel defekt?" << std::endl ;
        break;
    case blue:
    case gray:
    case white:
        std::cout << "Sonderfarb-Ampel" << std::endl ;
        break;
};

würde „ins Leere“ laufen.

Enumerationsobjekte lassen sich implizit in Ganzzahldatentypen umwandeln. Umgekehrt kann auch eine Ganzzahl in ein Objekt einer Enumeration umgewandelt werden. Hierfür ist allerdings eine explizite Angabe nötig und außerdem ist die Umwandlung nur standardkonform, wenn der Ganzzahlwert im Wertebereich der Enumeration liegt. Andernfalls ist das Verhalten undefiniert. Der Wertebereich einer Enumeration lässt sich aus ihren Indizes ableiten. Für Enumerationen, die nur positive Indizes besitzen liegt der Wertebereich zwischen 0 und der kleinsten Zweierpotenz, die größer als der größte Index ist. Für Enumerationen mit negativen Indizes muss auch der kleinste Wert größer oder gleich der größten negativen Zweierpotenz sein.

enum A{a, b, c = 15};           //   0 .. 15 ( 0  .. 2⁴ - 1)
enum B{v1 = 1, v2 = 2, v3 = 4}; //   0 .. 7  ( 0  .. 2³ - 1)
enum C{c1 = -10, c2 = 0};       // -16 .. 15 (-2⁴ .. 2⁴ - 1)
enum D{d1 = -10, d2 = 16};      // -32 .. 31 (-2⁵ .. 2⁵ - 1)

Die -1 im positiven Teil des Wertebereichs kommt zu Stande, da der positive Wertebereich die 0 beinhaltet, während bei Ganzzahlen keine negative 0 existiert. Die Enumeratoren können in der Umgebung ihrer Enumeration ohne Zugriffsoperator verwendet werden. Das folgende Beispiel wird dies demonstrieren.


#include <iostream>

enum color{
    red,
    green,
    blue
};

int main(){
    color actual_color = red;
    int input;

    // Eingabe bis input einen Wert hat, der einem der Indizes entspricht
    do{
        std::cin >> input;
    }while(input < red || input > blue);
    actual_color = color(input); // explizite Umwandlung nach color

    switch(actual_color){
        case red:   std::cout << "red"   << std::endl; break;
        case green: std::cout << "green" << std::endl; break;
        case blue:  std::cout << "blue"  << std::endl; break;
    }
}

In diesem Beispiel kann der Compiler eine Warnung ausgeben, falls nicht alle Enumeratoren in der switch-Anweisung angefragt wurden. Dies ist, neben der besseren Lesbarkeit des Codes, eine hilfreiche Ausgabe bei der Fehlersuche. Im folgenden Beispiel wird noch gezeigt, wie Enumeratoren genutzt werden können, um mehrere Boolesche Werte in einer Variablen unterzubringen.

#include <iostream>

enum font{
    italic    = 0x01,
    bold      = 0x02,
    underline = 0x04
}; // Wertebereich 0 .. 7

int main(){
    font flags = font(0); // Kein flag gesetzt
    char input;

    std::cout << "Kursivdruck? ";
    std::cin >> input;
    // Binäres Oder zum Setzen eines Flags (flags | italic => int)
    if(input == 'j') flags = font(flags | italic);

    std::cout << "Fettdruck? ";
    std::cin >> input;
    if(input == 'j') flags = font(flags | bold);

    std::cout << "Unterstreichen? ";
    std::cin >> input;
    if(input == 'j') flags = font(flags | underline);

    // Binäres Und zum Abfragen des Flags
    if(flags & italic){
        std::cout << "Der Text ist kursiv." << std::endl;
    }
    if(flags & bold){
        std::cout << "Der Text ist fettgedruckt." << std::endl;
    }
    if(flags & underline){
        std::cout << "Der Text ist unterstrichen." << std::endl;
    }
    if(flags == 0){
        std::cout << "Der Text wird normal ausgegeben." << std::endl;
    }
}