Deklaration einer Aufzählung innerhalb einer Klasse

150

Im folgenden Codeausschnitt wird die ColorAufzählung innerhalb der CarKlasse deklariert , um den Umfang der Aufzählung einzuschränken und zu versuchen, den globalen Namespace nicht zu "verschmutzen".

class Car
{
public:

   enum Color
   {
      RED,
      BLUE,
      WHITE
   };

   void SetColor( Car::Color color )
   {
      _color = color;
   }

   Car::Color GetColor() const
   {
      return _color;
   }

private:

   Car::Color _color;

};

(1) Ist dies ein guter Weg, um den Umfang der ColorAufzählung einzuschränken? Oder sollte ich es außerhalb der CarKlasse deklarieren , aber möglicherweise innerhalb ihres eigenen Namespace oder seiner eigenen Struktur? Ich bin heute gerade auf diesen Artikel gestoßen, der sich für Letzteres einsetzt und einige nette Punkte zu Aufzählungen bespricht: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) Ist es in diesem Beispiel am besten , wenn Sie innerhalb der Klasse arbeiten, die Aufzählung als zu codieren Car::Coloroder würde dies nur Colorausreichen? (Ich gehe davon aus, dass Ersteres besser ist, nur für den Fall, dass Colorim globalen Namespace eine andere Aufzählung deklariert ist. Auf diese Weise geben wir zumindest die Aufzählung an, auf die wir uns beziehen.)

bporter
quelle

Antworten:

85
  1. Wenn Colores etwas ist, das nur für Cars spezifisch ist, dann würden Sie seinen Umfang auf diese Weise einschränken. Wenn Sie eine andere ColorAufzählung haben möchten, die von anderen Klassen verwendet wird, können Sie sie auch global (oder zumindest außerhalb Car) erstellen .

  2. Es macht keinen Unterschied. Wenn es einen globalen gibt, wird der lokale trotzdem verwendet, da er näher am aktuellen Bereich liegt. Beachten Sie, dass Sie, wenn Sie diese Funktion außerhalb der Klassendefinition definieren, diese explizit Car::Colorin der Funktionsoberfläche angeben müssen .

Peter Alexander
quelle
12
2. Ja und nein. Car::Color getColor()aber void Car::setColor(Color c)weil in haben setColorwir schon den spezifizierer.
Matthieu M.
84

Heutzutage können Sie mit C ++ 11 die Enum-Klasse dafür verwenden:

enum class Color { RED, BLUE, WHITE };

AFAII das macht genau das was du willst.

Andreas Florath
quelle
2
Leider erlaubt es keine Mitgliedsfunktionen: stackoverflow.com/a/53284026/7395227
Andreas
66

Ich bevorzuge den folgenden Ansatz (Code unten). Es löst das Problem der "Namespace-Verschmutzung", ist aber auch viel typsicherer (Sie können nicht zwei verschiedene Aufzählungen oder Ihre Aufzählung mit anderen integrierten Typen usw. zuweisen und sogar vergleichen).

struct Color
{
    enum Type
    {
        Red, Green, Black
    };
    Type t_;
    Color(Type t) : t_(t) {}
    operator Type () const {return t_;}
private:
   //prevent automatic conversion for any other built-in types such as bool, int, etc
   template<typename T>
    operator T () const;
};

Verwendung:

Color c = Color::Red;
switch(c)
{
   case Color::Red:
     //некоторый код
   break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error

Ich erstelle ein Makro, um die Verwendung zu erleichtern:

#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
   enum type \
   { \
      BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
   }; \
   type v; \
   EnumName(type v) : v(v) {} \
   operator type() const {return v;} \
private: \
    template<typename T> \
    operator T () const;};\

#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
    BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),

Verwendung:

DEFINE_SIMPLE_ENUM(Color,
             ((Red, 1))
             ((Green, 3))
             )

Einige Referenzen:

  1. Herb Sutter, Jum Hyslop, C / C ++ - Benutzerjournal, 22 (5), Mai 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Stark typisierte Enums (Revision 3), Juli 2007
Sergey Teplyakov
quelle
Ich mag das. Außerdem wird die Aufzählung mit einem gültigen Wert instanziiert. Ich denke, ein Zuweisungsoperator und ein Kopierkonstruktor wären nützlich. Auch t_ sollte privat sein. Auf die Makros kann ich verzichten.
jmucchiello
Das gefällt mir auch. Danke für die Referenzen.
Anio
1
Sie sagten: "Außerdem ist es viel typsicherer (Sie können nicht zwei verschiedene Aufzählungen zuweisen und sogar vergleichen ..." . Warum halten Sie es für eine gute Funktion? Ich denke, es if(c2 == Color::Red )ist vernünftig und muss kompiliert werden, aber in Ihrem Beispiel nicht. Gleiches Argument für die Zuweisung auch!
Nawaz
3
@Nawaz c2ist von einem anderen Typ ( Color2). Warum sollten also c2 == Color::RedAufgaben kompiliert werden? Was Color::Redist, wenn 1 und Color2::Red2 ist? Sollte Color::Red == Color2::Redzu trueoder bewerten false? Wenn Sie nicht typsichere Enumeratoren mischen, werden Sie eine schlechte Zeit haben.
Victor K
2
Warum ist nicht Typ t_; Privat?
Zingam
7

Im Allgemeinen schreibe ich meine Aufzählungen immer in a struct. Ich habe mehrere Richtlinien gesehen, einschließlich "Präfix".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Ich habe immer gedacht, dass dies eher nach CRichtlinien als nach Richtlinien C++aussieht (zum einen wegen der Abkürzung und auch wegen der Namespaces in C++).

Um den Umfang einzuschränken, haben wir jetzt zwei Alternativen:

  • Namespaces
  • Strukturen / Klassen

Ich persönlich neige dazu, a zu verwenden, structda es als Parameter für die Vorlagenprogrammierung verwendet werden kann, während ein Namespace nicht manipuliert werden kann.

Beispiele für Manipulationen sind:

template <class T>
size_t number() { /**/ }

Dies gibt die Anzahl der Elemente von enum innerhalb der Struktur zurück T:)

Matthieu M.
quelle
3

Wenn Sie eine Codebibliothek erstellen, würde ich den Namespace verwenden. Sie können jedoch immer noch nur eine Farbaufzählung in diesem Namespace haben. Wenn Sie eine Aufzählung benötigen, die möglicherweise einen gemeinsamen Namen verwendet, jedoch unterschiedliche Konstanten für unterschiedliche Klassen aufweist, verwenden Sie Ihren Ansatz.

Harvey
quelle