Oft braucht man mehrere aufgezählte Typen zusammen. Manchmal hat man einen Namenskonflikt. Hierfür kommen zwei Lösungen in den Sinn: Verwenden Sie einen Namespace oder verwenden Sie 'größere' Enum-Elementnamen. Die Namespace-Lösung verfügt jedoch über zwei mögliche Implementierungen: eine Dummy-Klasse mit verschachtelter Aufzählung oder einen vollständigen Namespace.
Ich suche nach Vor- und Nachteilen aller drei Ansätze.
Beispiel:
// oft seen hand-crafted name clash solution
enum eColors { cRed, cColorBlue, cGreen, cYellow, cColorsEnd };
enum eFeelings { cAngry, cFeelingBlue, cHappy, cFeelingsEnd };
void setPenColor( const eColors c ) {
switch (c) {
default: assert(false);
break; case cRed: //...
break; case cColorBlue: //...
//...
}
}
// (ab)using a class as a namespace
class Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
class Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
// a real namespace?
namespace Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
namespace Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
enum e {...}
benennen müssen. Aufzählungen können anonym sein, dhenum {...}
, was in Namespace oder Klasse viel sinnvoller ist.Antworten:
Ursprüngliche C ++ 03 Antwort:
Der Vorteil von a
namespace
(über aclass
) besteht darin, dass Sieusing
Deklarationen verwenden können, wenn Sie möchten.Das Problem bei der Verwendung von a
namespace
besteht darin, dass Namespaces an anderer Stelle im Code erweitert werden können. In einem großen Projekt kann nicht garantiert werden, dass zwei unterschiedliche Aufzählungen nicht glauben, dass sie aufgerufen werdeneFeelings
Für einfacheren Code verwende ich a
struct
, da Sie vermutlich möchten, dass der Inhalt öffentlich ist.Wenn Sie eine dieser Praktiken ausführen, sind Sie der Kurve voraus und müssen dies wahrscheinlich nicht weiter untersuchen.
Neuere C ++ 11-Ratschläge:
Wenn Sie C ++ 11 oder höher verwenden,
enum class
werden die Aufzählungswerte implizit innerhalb des Aufzählungsnamens erfasst.Mit
enum class
verlieren Sie implizite Konvertierungen und Vergleiche mit ganzzahligen Typen, aber in der Praxis kann dies Ihnen helfen, mehrdeutigen oder fehlerhaften Code zu entdecken.quelle
Zu Ihrer Information In C ++ 0x gibt es eine neue Syntax für Fälle wie den von Ihnen erwähnten (siehe C ++ 0x-Wiki-Seite ).
quelle
Ich habe die vorhergehenden Antworten auf etwa Folgendes hybridisiert: (BEARBEITEN: Dies ist nur für Pre-C ++ 11 nützlich. Wenn Sie C ++ 11 verwenden, verwenden Sie
enum class
)Ich habe eine große Header-Datei, die alle meine Projektaufzählungen enthält, da diese Aufzählungen von Arbeiterklassen gemeinsam genutzt werden und es nicht sinnvoll ist, die Aufzählungen in die Arbeiterklassen selbst einzufügen.
Das
struct
vermeidet die Öffentlichkeit: syntaktischen Zucker, und dastypedef
lässt Sie tatsächlich Variablen dieser Aufzählungen innerhalb anderer Arbeiterklassen deklarieren.Ich denke nicht, dass die Verwendung eines Namespace überhaupt hilft. Vielleicht ist dies , weil ich ein C # Programmierer sind, und da Sie haben , um den Aufzählungstyp Namen zu verwenden , wenn die Werte Bezug genommen wird , so dass ich es gewohnt bin.
...
quelle
Ich würde definitiv vermeiden, eine Klasse dafür zu verwenden; Verwenden Sie stattdessen einen Namespace. Die Frage läuft darauf hinaus, ob ein Namespace oder eindeutige IDs für die Enum-Werte verwendet werden sollen. Persönlich würde ich einen Namespace verwenden, damit meine IDs kürzer und hoffentlich selbsterklärender sind. Dann könnte der Anwendungscode eine Direktive "using namespace" verwenden und alles lesbarer machen.
Aus Ihrem obigen Beispiel:
quelle
Colors someColor = Red;
, da der Namespace keinen Typ darstellt. Sie müsstenColors::e someColor = Red;
stattdessen schreiben , was ziemlich kontraintuitiv ist.Colors::e someColor
auch mit a verwenden,struct/class
wenn Sie es in einerswitch
Anweisung verwenden möchten ? Wenn Sie eine anonyme verwenden, kannenum
der Switch a nicht auswertenstruct
.const e c
scheint mir aber kaum lesbar zu sein :-) Tu das nicht. Die Verwendung eines Namespace ist jedoch in Ordnung.Ein Unterschied zwischen der Verwendung einer Klasse oder eines Namespace besteht darin, dass die Klasse nicht wie ein Namespace erneut geöffnet werden kann. Dies vermeidet die Möglichkeit, dass der Namespace in Zukunft missbraucht wird, aber es gibt auch das Problem, das Sie auch nicht zu den Aufzählungen hinzufügen können.
Ein möglicher Vorteil bei der Verwendung einer Klasse besteht darin, dass sie als Argumente vom Typ Vorlage verwendet werden können, was bei Namespaces nicht der Fall ist:
Persönlich bin ich kein Fan von Verwendung , und ich bevorzuge die vollqualifizierten Namen, daher sehe ich das nicht wirklich als Plus für Namespaces. Dies ist jedoch wahrscheinlich nicht die wichtigste Entscheidung, die Sie in Ihrem Projekt treffen werden!
quelle
Der Vorteil der Verwendung einer Klasse besteht darin, dass Sie eine vollwertige Klasse darauf aufbauen können.
Wie das obige Beispiel zeigt, können Sie mit einer Klasse:
Beachten Sie nur, dass Sie deklarieren müssen,
operator enum_type()
damit C ++ weiß, wie Ihre Klasse in eine zugrunde liegende Aufzählung konvertiert wird. Andernfalls können Sie den Typ nicht an eineswitch
Anweisung übergeben.quelle
Da Aufzählungen auf ihren umschließenden Bereich beschränkt sind, ist es wahrscheinlich am besten, sie in etwas einzuwickeln , um eine Verschmutzung des globalen Namespace und eine Vermeidung von Namenskollisionen zu vermeiden. Ich bevorzuge einen Namespace gegenüber dem Unterricht, nur weil er
namespace
sich wie ein Sack voll Haltclass
anfühlt , während er sich wie ein robustes Objekt anfühlt (vgl. Diestruct
vs.-class
Debatte). Ein möglicher Vorteil eines Namespace besteht darin, dass er später erweitert werden kann - nützlich, wenn Sie mit Code von Drittanbietern arbeiten, den Sie nicht ändern können.Dies ist natürlich alles umstritten, wenn wir Enum-Klassen mit C ++ 0x erhalten.
quelle
Ich neige auch dazu, meine Aufzählungen in Klassen zu wickeln.
Wie von Richard Corden signalisiert, besteht der Vorteil einer Klasse darin, dass es sich um einen Typ im c ++ - Sinne handelt und Sie ihn daher mit Vorlagen verwenden können.
Ich habe eine spezielle Toolbox :: Enum-Klasse für meine Anforderungen, die ich auf alle Vorlagen spezialisiert habe, die grundlegende Funktionen bereitstellen (hauptsächlich: Zuordnen eines Enum-Werts zu einem std :: string, damit E / A leichter zu lesen sind).
Meine kleine Vorlage hat auch den zusätzlichen Vorteil, dass sie wirklich nach den zulässigen Werten sucht. Der Compiler prüft, ob der Wert wirklich in der Aufzählung enthalten ist:
Es hat mich immer gestört, dass der Compiler dies nicht abfängt, da Sie einen Aufzählungswert haben, der keinen Sinn hat (und den Sie nicht erwarten).
Ähnlich:
Erneut gibt main einen Fehler zurück.
Das Problem ist, dass der Compiler die Aufzählung in die kleinste verfügbare Darstellung einfügt (hier benötigen wir 2 Bits) und dass alles, was in diese Darstellung passt, als gültiger Wert betrachtet wird.
Es gibt auch das Problem, dass Sie manchmal lieber eine Schleife für die möglichen Werte anstelle eines Schalters haben möchten, damit Sie nicht jedes Mal, wenn Sie der Aufzählung einen Wert hinzufügen, alle Schalter ändern müssen.
Alles in allem erleichtert mein kleiner Helfer meine Aufzählungen wirklich (natürlich erhöht dies den Aufwand) und es ist nur möglich, weil ich jede Aufzählung in einer eigenen Struktur verschachtele :)
quelle