Dichiarare un enum all'interno di una classe


151

Nel seguente frammento di codice, l' Colorenum viene dichiarato all'interno della Carclasse al fine di limitare l'ambito dell'enum e cercare di non "inquinare" lo spazio dei nomi globale.

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) È un buon modo per limitare la portata Colordell'enum? O dovrei dichiararlo al di fuori della Carclasse, ma possibilmente all'interno del suo spazio dei nomi o struttura? Ho appena trovato questo articolo oggi, che sostiene quest'ultimo e discute alcuni bei punti sugli enum: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) In questo esempio, quando si lavora all'interno della classe, è meglio codificare l'enum come Car::Coloro sarebbe Colorsufficiente? (Suppongo che il primo sia migliore, nel caso in cui ci sia un altro Colorenum dichiarato nello spazio dei nomi globale. In questo modo, almeno, siamo espliciti sull'enum a cui ci stiamo riferendo.)

Risposte:


86
  1. Se Colorè qualcosa che è specifico solo per Cars, questo è il modo in cui ne limiteresti l'ambito. Se hai un altro Colorenum che altre classi usano, potresti anche renderlo globale (o almeno esterno Car).

  2. Non fa differenza. Se ce n'è uno globale, quello locale viene comunque utilizzato in quanto è più vicino all'ambito attuale. Si noti che se si definiscono tali funzioni al di fuori della definizione della classe, sarà necessario specificare esplicitamente Car::Colornell'interfaccia della funzione.


12
2. Sì e no. Car::Color getColor()ma void Car::setColor(Color c)perché in setColorabbiamo già l'identificatore.
Matthieu M.


66

Preferisco seguire l'approccio (codice sotto). Risolve il problema dell '"inquinamento dello spazio dei nomi", ma è anche molto più semplice (non puoi assegnare e nemmeno confrontare due diverse enumerazioni o la tua enumerazione con altri tipi predefiniti, ecc.).

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;
};

Uso:

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

Creo macro per facilitare l'utilizzo:

#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),

Uso:

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

Alcuni riferimenti:

  1. Herb Sutter, Jum Hyslop, C / C ++ Users Journal, 22 (5), maggio 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Enumerazioni fortemente tipizzate (revisione 3), luglio 2007

Mi piace questo. Forza anche l'istanza di essere istanziata con un valore valido. Penso che sarebbe utile un operatore di assegnazione e un costruttore di copie. Anche t_ dovrebbe essere privato. Le macro di cui posso fare a meno.
jmucchiello,

Anche a me piace questo. Grazie per i riferimenti.
anio,

1
Hai detto: "inoltre è molto più typesafe (non puoi assegnare e nemmeno confrontare due diverse enumerazioni ..." . Perché pensi che sia una buona funzionalità? Penso che if(c2 == Color::Red )sia ragionevole e debba essere compilato, ma nel tuo esempio lo è no. Stesso argomento anche per l'incarico!
Nawaz

3
@Nawaz c2è di un altro tipo ( Color2), quindi perché pensi c2 == Color::Redche i compiti dovrebbero essere compilati? Cosa succede se Color::Redè 1 ed Color2::Redè 2? Dovrebbe Color::Red == Color2::Redvalutare trueo false? Se mescoli enumeratori non tipografici, avrai un brutto momento.
Victor K

2
Perché non digitare t_; privato?
Zingam,

7

In generale, ho sempre messo le mie enumerazioni in a struct. Ho visto diverse linee guida tra cui "prefisso".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Ho sempre pensato che questo assomigliava più a Clinee guida che a C++quelle (per uno a causa dell'abbreviazione e anche per gli spazi dei nomi in C++).

Quindi per limitare l'ambito abbiamo ora due alternative:

  • spazi dei nomi
  • struct / classes

Personalmente tendo a usare a structperché può essere usato come parametro per la programmazione dei template mentre uno spazio dei nomi non può essere manipolato.

Esempi di manipolazione includono:

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

che restituisce il numero di elementi di enum all'interno della struttura T:)


3

Se stai creando una libreria di codici, utilizzerei lo spazio dei nomi. Tuttavia, puoi comunque avere solo un enum Color all'interno di quello spazio dei nomi. Se hai bisogno di un enum che potrebbe usare un nome comune, ma potrebbe avere costanti diverse per classi diverse, usa il tuo approccio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.