Wenn jemand behauptet, ein Programmierkonstrukt sei "böse", versucht er, Sie davon abzuhalten, selbst zu denken.
Pete Becker
3
@NicolBolas: Dies ist eher eine rethorische Frage, um eine FAQ-Antwort zu geben (ob dies wirklich häufig gestellt wird , ist eine andere Geschichte).
David Rodríguez - Dribeas
@ David, es gibt eine Diskussion, ob dies eine FAQ sein soll oder nicht, die hier beginnt . Eingabe willkommen.
sbi
17
@PeteBecker Manchmal versuchen sie nur, dich vor dir selbst zu schützen .
Piccy
geeksforgeeks.org/... Dies ist auch ein guter Ort , um zu verstehen enumvs enum class.
mr_azad
Antworten:
471
C ++ hat zwei Arten von enum:
enum classes
Plain enums
Hier sind einige Beispiele, wie man sie deklariert:
enumclassColor{ red, green, blue };// enum classenumAnimal{ dog, cat, bird, human };// plain enum
Was ist der Unterschied zwischen zwei?
enum classes - Enumerator-Namen sind lokal für die Aufzählung und ihre Werte werden nicht implizit in andere Typen konvertiert (wie andere enumoder int)
Einfache enums - wobei Enumeratornamen im selben Bereich wie die Aufzählung liegen und ihre Werte implizit in Ganzzahlen und andere Typen konvertiert werden
Beispiel:
enumColor{ red, green, blue };// plain enum enumCard{ red_card, green_card, yellow_card };// another plain enum enumclassAnimal{ dog, deer, cat, bird, human };// enum classenumclassMammal{ kangaroo, deer, human };// another enum classvoid fun(){// examples of bad use of plain enums:Color color =Color::red;Card card =Card::green_card;int num = color;// no problemif(color ==Card::red_card)// no problem (bad)
cout <<"bad"<< endl;if(card ==Color::green)// no problem (bad)
cout <<"bad"<< endl;// examples of good use of enum classes (safe)Animal a =Animal::deer;Mammal m =Mammal::deer;int num2 = a;// errorif(m == a)// error (good)
cout <<"bad"<< endl;if(a ==Mammal::deer)// error (good)
cout <<"bad"<< endl;}
Fazit:
enum classEs sollte bevorzugt werden, da sie weniger Überraschungen verursachen, die möglicherweise zu Fehlern führen können.
Gutes Beispiel ... gibt es eine Möglichkeit, die Typensicherheit der Klassenversion mit der Namespace-Promotion der Enum-Version zu kombinieren? Das heißt, wenn ich eine Klasse Amit Status habe und enum class State { online, offline };als Kind eine Klasse erstelle A, möchte ich state == onlineinnerhalb Avon state == State::online... Prüfungen durchführen, anstatt ... ist das möglich?
Markieren Sie den
31
Nee. Die Namespace-Promotion ist ein Bad Thing ™ und die halbe Rechtfertigung dafür enum classwar, sie zu beseitigen.
Welpe
10
In C ++ 11 können Sie auch explizit typisierte Aufzählungen verwenden, wie z. B. Aufzählung Animal: unsigned int {Hund, Hirsch, Katze, Vogel}
Blasius Secundus
3
@Cat Plus Plus Ich verstehe, dass @Oleksiy sagt, dass es schlecht ist. Meine Frage war nicht, ob Oleksiy es für schlecht hielt. Meine Frage war eine Bitte, genau zu beschreiben, was daran schlecht ist. Insbesondere, warum hält Oleksiy zum Beispiel schlecht für schlecht Color color = Color::red.
chux
9
@Cat Plus Plus Das schlechte Beispiel tritt also erst in der if (color == Card::red_card)Zeile auf, 4 Zeilen später als der Kommentar (der jetzt für die erste Hälfte des Blocks gilt). 2 Zeilen des Blocks geben die schlechten Beispiele an. Die ersten 3 Zeilen sind kein Problem. Der "ganze Block ist, warum einfache Aufzählungen schlecht sind" warf mich, als ich dachte, Sie meinten, dass etwas mit diesen auch nicht stimmte. Ich sehe jetzt, es ist nur ein Setup. Auf jeden Fall danke für das Feedback.
Die enum classes ("neue Aufzählungen", "starke Aufzählungen") behandeln drei Probleme mit herkömmlichen C ++ - Aufzählungen:
Herkömmliche Aufzählungen werden implizit in int konvertiert und verursachen Fehler, wenn jemand nicht möchte, dass eine Aufzählung als Ganzzahl fungiert.
Herkömmliche Aufzählungen exportieren ihre Enumeratoren in den umgebenden Bereich, was zu Namenskonflikten führt.
Der zugrunde liegende Typ von a enumkann nicht angegeben werden, was zu Verwirrung und Kompatibilitätsproblemen führt und eine Vorwärtsdeklaration unmöglich macht.
Die neuen Aufzählungen sind "Aufzählungsklassen", da sie Aspekte traditioneller Aufzählungen (Namenswerte) mit Aspekten von Klassen (Gültigkeitsbereichsmitglieder und fehlende Konvertierungen) kombinieren.
Wie von anderen Benutzern erwähnt, würden die "starken Aufzählungen" den Code sicherer machen.
Der zugrunde liegende Typ eines "Klassikers" enummuss ein ganzzahliger Typ sein, der groß genug ist, um alle Werte des zu enumerfüllen. Dies ist in der Regel eine int. Außerdem muss jeder Aufzählungstyp mit chareinem vorzeichenbehafteten / vorzeichenlosen Ganzzahltyp kompatibel sein .
Dies ist eine umfassende Beschreibung dessen, was ein enumzugrunde liegender Typ sein muss, sodass jeder Compiler selbst Entscheidungen über den zugrunde liegenden Typ des Klassikers treffen kann enumund das Ergebnis manchmal überraschend sein kann.
Zum Beispiel habe ich Code wie diesen ein paar Mal gesehen:
enum E_MY_FAVOURITE_FRUITS
{
E_APPLE =0x01,
E_WATERMELON =0x02,
E_COCONUT =0x04,
E_STRAWBERRY =0x08,
E_CHERRY =0x10,
E_PINEAPPLE =0x20,
E_BANANA =0x40,
E_MANGO =0x80,
E_MY_FAVOURITE_FRUITS_FORCE8 =0xFF// 'Force' 8bits, how can you tell?};
Im obigen Code denkt ein naiver Codierer, dass der Compiler die E_MY_FAVOURITE_FRUITSWerte in einem vorzeichenlosen 8-Bit-Typ speichert ... aber es gibt keine Garantie dafür: Der Compiler kann wählen unsigned charoder intoder short, jeder dieser Typen ist groß genug, um alle zu passen Werte in der gesehen enum. Das Hinzufügen des Feldes E_MY_FAVOURITE_FRUITS_FORCE8ist eine Belastung und zwingt den Compiler nicht dazu, eine Auswahl über den zugrunde liegenden Typ des zu treffen enum.
Wenn es einen Code gibt, der von der Typgröße abhängt und / oder davon ausgeht, dass er E_MY_FAVOURITE_FRUITSeine gewisse Breite hat (z. B. Serialisierungsroutinen), kann sich dieser Code abhängig von den Gedanken des Compilers auf seltsame Weise verhalten.
Und um die Sache noch schlimmer zu machen, wenn ein Arbeitskollege nachlässig einen neuen Wert für unsere enum:
E_DEVIL_FRUIT =0x100,// New fruit, with value greater than 8bits
Der Compiler beschwert sich nicht darüber! Der Typ wird nur so angepasst, dass er allen Werten von entspricht enum(vorausgesetzt, der Compiler verwendet den kleinstmöglichen Typ, was eine Annahme ist, die wir nicht tun können). Diese einfache und nachlässige Ergänzung des enumCodes könnte den Code subtil beschädigen.
Da es in C ++ 11 möglich ist, den zugrunde liegenden Typ für enumund enum class(danke rdb ) anzugeben , wird dieses Problem sorgfältig behoben :
Wenn der Compiler einen Ausdruck außerhalb des Bereichs dieses Typs angibt, beschwert sich der Compiler, anstatt den zugrunde liegenden Typ zu ändern.
Ich denke, dass dies eine gute Sicherheitsverbesserung ist.
So Warum ist Enum Klasse über Normal Enum bevorzugt? , wenn wir den zugrunde liegenden Typ für Aufzählungen mit Gültigkeitsbereich ( enum class) und ohne Gültigkeitsbereich ( enum) auswählen können, was macht dann noch enum classeine bessere Wahl?:
Ich nehme an, wir können den Enum-Basistyp auch für reguläre Enums einschränken, solange wir C ++ 11
Sagar Padhye
11
Entschuldigung, aber diese Antwort ist falsch. "enum class" hat nichts mit der Möglichkeit zu tun, den Typ anzugeben. Dies ist eine unabhängige Funktion, die sowohl für reguläre Aufzählungen als auch für Aufzählungsklassen vorhanden ist.
Rdb
14
Dies ist der Deal: * Enum-Klassen sind eine neue Funktion in C ++ 11. * Typisierte Aufzählungen sind eine neue Funktion in C ++ 11. Dies sind zwei separate, nicht verwandte neue Funktionen in C ++ 11. Sie können beide verwenden, oder Sie können entweder oder keine verwenden.
Rdb
2
Ich denke, dass Alex Allain die vollständigste einfache Erklärung liefert, die ich bisher in diesem Blog unter [ cprogramming.com/c++11/… gesehen habe . Die herkömmliche Aufzählung war gut geeignet, um Namen anstelle von ganzzahligen Werten zu verwenden und die Verwendung von Präprozessor #defines zu vermeiden, was eine gute Sache war - es fügte Klarheit hinzu. Enum - Klasse entfernt das Konzept eines numerischen Werts des Enumerators und führt Umfang und starke Typisierung zunimmt (na ja, kann erhöhen :-) Programm Korrektheit. Es bringt Sie dem objektorientierten Denken einen Schritt näher.
Jon Spencer
2
Abgesehen davon ist es immer amüsant, wenn Sie Code überprüfen und plötzlich One Piece passiert.
Justin Time - Stellen Sie Monica
47
Der grundlegende Vorteil der Verwendung der Enum-Klasse gegenüber normalen Enums besteht darin, dass Sie möglicherweise dieselben Enum-Variablen für 2 verschiedene Enums haben und diese dennoch auflösen können (was von OP als typsicher erwähnt wurde).
Zum Beispiel:
enumclassColor1{ red, green, blue };//this will compileenumclassColor2{ red, green, blue };enumColor1{ red, green, blue };//this will not compile enumColor2{ red, green, blue };
Bei den Basisaufzählungen kann der Compiler nicht unterscheiden, ob er redsich auf den Typ bezieht Color1oder Color2wie in der folgenden Anweisung beschrieben.
enumColor1{ red, green, blue };enumColor2{ red, green, blue };int x = red;//Compile time error(which red are you refering to??)
@Oleksiy Ohh, ich habe deine Frage nicht richtig gelesen. Betrachten Sie es als Add-On für diejenigen, die es nicht wussten.
Saksham
Es ist in Ordnung! Ich hätte das fast vergessen
Oleksiy
Natürlich würden Sie schreiben enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }und Namespace-Probleme leicht vermeiden. Das Namespace-Argument ist eines der drei hier genannten, die ich überhaupt nicht kaufe.
Jo So
2
@ Jo So Diese Lösung ist eine unnötige Problemumgehung. Aufzählung: enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }ist vergleichbar mit Aufzählungsklasse : enum class Color1 { RED, GREEN, BLUE }. Der Zugriff ist ähnlich: COLOR1_REDvs Color1::RED, aber in der Enum-Version müssen Sie in jeden Wert "COLOR1" eingeben, wodurch mehr Platz für Tippfehler vorhanden ist, was durch das Namespace-Verhalten einer Enum-Klasse vermieden wird.
CDgraham
2
Bitte konstruktive Kritik üben . Wenn ich mehr Platz für Tippfehler sage, meine ich, wenn Sie ursprünglich die Werte definieren, deren Werte enum Color1ein Compiler nicht erfassen kann, da es sich wahrscheinlich immer noch um einen "gültigen" Namen handelt. Wenn ich schreibe RED, GREENund so weiter eine ENUM - Klasse, als es nicht lösen , enum Bananaweil es erfordert , dass Sie angeben , Color1::REDum den Wert zuzugreifen (der Namensraum Argument). Es gibt immer noch gute Zeiten enum, aber das Namespace-Verhalten von enum classkann oft sehr vorteilhaft sein.
CDgraham
20
Aufzählungen werden verwendet, um eine Reihe von ganzzahligen Werten darzustellen.
Das classSchlüsselwort nach dem enumgibt an, dass die Aufzählung stark typisiert ist und ihre Aufzähler einen Gültigkeitsbereich haben. Auf diese Weise enumverhindern Klassen einen versehentlichen Missbrauch von Konstanten.
Herkömmliche Aufzählungen werden implizit in int konvertiert und verursachen Fehler, wenn jemand nicht möchte, dass eine Aufzählung als Ganzzahl fungiert.
enum color
{Red,Green,Yellow};enumclassNewColor{Red_1,Green_1,Yellow_1};int main(){//! Implicit conversion is possibleint i =Red;//! Need enum class name followed by access specifier. Ex: NewColor::Red_1int j =Red_1;// error C2065: 'Red_1': undeclared identifier//! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1;int k =NewColor::Red_1;// error C2440: 'initializing': cannot convert from 'NewColor' to 'int'return0;}
Herkömmliche Aufzählungen exportieren ihre Enumeratoren in den umgebenden Bereich, was zu Namenskonflikten führt.
// Header.henum vehicle
{Car,Bus,Bike,Autorickshow};enumFourWheeler{Car,// error C2365: 'Car': redefinition; previous definition was 'enumerator'SmallBus};enumclassEditor{
vim,
eclipes,VisualStudio};enumclassCppEditor{
eclipes,// No error of redefinitionsVisualStudio,// No error of redefinitionsQtCreator};
Der zugrunde liegende Typ einer Aufzählung kann nicht angegeben werden, was zu Verwirrung und Kompatibilitätsproblemen führt und eine Vorwärtsdeklaration unmöglich macht.
Da Klassenaufzählungen, wie in anderen Antworten erwähnt, nicht implizit in int / bool konvertierbar sind, hilft es auch, fehlerhaften Code wie den folgenden zu vermeiden:
enumMyEnum{Value1,Value2,};...if(var ==Value1||Value2)// Should be "var == Value2" no error/warning
Um meinen vorherigen Kommentar zu vervollständigen, beachten Sie, dass gcc jetzt eine Warnung namens -Wint-in-bool-context hat, die genau diese Art von Fehlern abfängt.
Arnaud
1
Zusätzlich zu diesen anderen Antworten ist anzumerken, dass C ++ 20 eines der Probleme löst, die es enum classgibt: Ausführlichkeit. Imagining eine hypothetische enum class, Color.
Dies ist ausführlich im Vergleich zu der einfachen enumVariante, bei der die Namen im globalen Bereich liegen und daher nicht vorangestellt werden müssen Color::.
In C ++ 20 können wir jedoch using enumalle Namen in einer Aufzählung in den aktuellen Bereich einfügen, um das Problem zu lösen.
Eine Sache, die nicht explizit erwähnt wurde - die Bereichsfunktion bietet Ihnen die Möglichkeit, denselben Namen für eine Aufzählungs- und Klassenmethode zu verwenden. Zum Beispiel:
classTest{public:// these call ProcessCommand() internallyvoidTakeSnapshot();voidRestoreSnapshot();private:enumclassCommand// wouldn't be possible without 'class'{TakeSnapshot,RestoreSnapshot};voidProcessCommand(Command cmd);// signal the other thread or whatever};
enum
vsenum class
.Antworten:
C ++ hat zwei Arten von
enum
:enum class
esenum
sHier sind einige Beispiele, wie man sie deklariert:
Was ist der Unterschied zwischen zwei?
enum class
es - Enumerator-Namen sind lokal für die Aufzählung und ihre Werte werden nicht implizit in andere Typen konvertiert (wie andereenum
oderint
)Einfache
enum
s - wobei Enumeratornamen im selben Bereich wie die Aufzählung liegen und ihre Werte implizit in Ganzzahlen und andere Typen konvertiert werdenBeispiel:
Fazit:
enum class
Es sollte bevorzugt werden, da sie weniger Überraschungen verursachen, die möglicherweise zu Fehlern führen können.quelle
A
mit Status habe undenum class State { online, offline };
als Kind eine Klasse erstelleA
, möchte ichstate == online
innerhalbA
vonstate == State::online
... Prüfungen durchführen, anstatt ... ist das möglich?enum class
war, sie zu beseitigen.Color color = Color::red
.if (color == Card::red_card)
Zeile auf, 4 Zeilen später als der Kommentar (der jetzt für die erste Hälfte des Blocks gilt). 2 Zeilen des Blocks geben die schlechten Beispiele an. Die ersten 3 Zeilen sind kein Problem. Der "ganze Block ist, warum einfache Aufzählungen schlecht sind" warf mich, als ich dachte, Sie meinten, dass etwas mit diesen auch nicht stimmte. Ich sehe jetzt, es ist nur ein Setup. Auf jeden Fall danke für das Feedback.Aus den C ++ 11-FAQ von Bjarne Stroustrup :
Wie von anderen Benutzern erwähnt, würden die "starken Aufzählungen" den Code sicherer machen.
Der zugrunde liegende Typ eines "Klassikers"
enum
muss ein ganzzahliger Typ sein, der groß genug ist, um alle Werte des zuenum
erfüllen. Dies ist in der Regel eineint
. Außerdem muss jeder Aufzählungstyp mitchar
einem vorzeichenbehafteten / vorzeichenlosen Ganzzahltyp kompatibel sein .Dies ist eine umfassende Beschreibung dessen, was ein
enum
zugrunde liegender Typ sein muss, sodass jeder Compiler selbst Entscheidungen über den zugrunde liegenden Typ des Klassikers treffen kannenum
und das Ergebnis manchmal überraschend sein kann.Zum Beispiel habe ich Code wie diesen ein paar Mal gesehen:
Im obigen Code denkt ein naiver Codierer, dass der Compiler die
E_MY_FAVOURITE_FRUITS
Werte in einem vorzeichenlosen 8-Bit-Typ speichert ... aber es gibt keine Garantie dafür: Der Compiler kann wählenunsigned char
oderint
odershort
, jeder dieser Typen ist groß genug, um alle zu passen Werte in der gesehenenum
. Das Hinzufügen des FeldesE_MY_FAVOURITE_FRUITS_FORCE8
ist eine Belastung und zwingt den Compiler nicht dazu, eine Auswahl über den zugrunde liegenden Typ des zu treffenenum
.Wenn es einen Code gibt, der von der Typgröße abhängt und / oder davon ausgeht, dass er
E_MY_FAVOURITE_FRUITS
eine gewisse Breite hat (z. B. Serialisierungsroutinen), kann sich dieser Code abhängig von den Gedanken des Compilers auf seltsame Weise verhalten.Und um die Sache noch schlimmer zu machen, wenn ein Arbeitskollege nachlässig einen neuen Wert für unsere
enum
:Der Compiler beschwert sich nicht darüber! Der Typ wird nur so angepasst, dass er allen Werten von entspricht
enum
(vorausgesetzt, der Compiler verwendet den kleinstmöglichen Typ, was eine Annahme ist, die wir nicht tun können). Diese einfache und nachlässige Ergänzung desenum
Codes könnte den Code subtil beschädigen.Da es in C ++ 11 möglich ist, den zugrunde liegenden Typ für
enum
undenum class
(danke rdb ) anzugeben , wird dieses Problem sorgfältig behoben :Wenn der Compiler einen Ausdruck außerhalb des Bereichs dieses Typs angibt, beschwert sich der Compiler, anstatt den zugrunde liegenden Typ zu ändern.
Ich denke, dass dies eine gute Sicherheitsverbesserung ist.
So Warum ist Enum Klasse über Normal Enum bevorzugt? , wenn wir den zugrunde liegenden Typ für Aufzählungen mit Gültigkeitsbereich (
enum class
) und ohne Gültigkeitsbereich (enum
) auswählen können, was macht dann nochenum class
eine bessere Wahl?:int
.quelle
Der grundlegende Vorteil der Verwendung der Enum-Klasse gegenüber normalen Enums besteht darin, dass Sie möglicherweise dieselben Enum-Variablen für 2 verschiedene Enums haben und diese dennoch auflösen können (was von OP als typsicher erwähnt wurde).
Zum Beispiel:
Bei den Basisaufzählungen kann der Compiler nicht unterscheiden, ob er
red
sich auf den Typ beziehtColor1
oderColor2
wie in der folgenden Anweisung beschrieben.quelle
enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }
und Namespace-Probleme leicht vermeiden. Das Namespace-Argument ist eines der drei hier genannten, die ich überhaupt nicht kaufe.enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }
ist vergleichbar mit Aufzählungsklasse :enum class Color1 { RED, GREEN, BLUE }
. Der Zugriff ist ähnlich:COLOR1_RED
vsColor1::RED
, aber in der Enum-Version müssen Sie in jeden Wert "COLOR1" eingeben, wodurch mehr Platz für Tippfehler vorhanden ist, was durch das Namespace-Verhalten einer Enum-Klasse vermieden wird.enum Color1
ein Compiler nicht erfassen kann, da es sich wahrscheinlich immer noch um einen "gültigen" Namen handelt. Wenn ich schreibeRED
,GREEN
und so weiter eine ENUM - Klasse, als es nicht lösen ,enum Banana
weil es erfordert , dass Sie angeben ,Color1::RED
um den Wert zuzugreifen (der Namensraum Argument). Es gibt immer noch gute Zeitenenum
, aber das Namespace-Verhalten vonenum class
kann oft sehr vorteilhaft sein.Aufzählungen werden verwendet, um eine Reihe von ganzzahligen Werten darzustellen.
Das
class
Schlüsselwort nach demenum
gibt an, dass die Aufzählung stark typisiert ist und ihre Aufzähler einen Gültigkeitsbereich haben. Auf diese Weiseenum
verhindern Klassen einen versehentlichen Missbrauch von Konstanten.Zum Beispiel:
Hier können wir keine Tier- und Haustierwerte mischen.
quelle
In den häufig gestellten Fragen zu C ++ 11 werden folgende Punkte erwähnt:
Herkömmliche Aufzählungen werden implizit in int konvertiert und verursachen Fehler, wenn jemand nicht möchte, dass eine Aufzählung als Ganzzahl fungiert.
Herkömmliche Aufzählungen exportieren ihre Enumeratoren in den umgebenden Bereich, was zu Namenskonflikten führt.
Der zugrunde liegende Typ einer Aufzählung kann nicht angegeben werden, was zu Verwirrung und Kompatibilitätsproblemen führt und eine Vorwärtsdeklaration unmöglich macht.
.
.
quelle
quelle
Da Klassenaufzählungen, wie in anderen Antworten erwähnt, nicht implizit in int / bool konvertierbar sind, hilft es auch, fehlerhaften Code wie den folgenden zu vermeiden:
quelle
Zusätzlich zu diesen anderen Antworten ist anzumerken, dass C ++ 20 eines der Probleme löst, die es
enum class
gibt: Ausführlichkeit. Imagining eine hypothetischeenum class
,Color
.Dies ist ausführlich im Vergleich zu der einfachen
enum
Variante, bei der die Namen im globalen Bereich liegen und daher nicht vorangestellt werden müssenColor::
.In C ++ 20 können wir jedoch
using enum
alle Namen in einer Aufzählung in den aktuellen Bereich einfügen, um das Problem zu lösen.Es gibt also keinen Grund, nicht zu verwenden
enum class
.quelle
Eine Sache, die nicht explizit erwähnt wurde - die Bereichsfunktion bietet Ihnen die Möglichkeit, denselben Namen für eine Aufzählungs- und Klassenmethode zu verwenden. Zum Beispiel:
quelle