174 votes

Déclarer un enum dans une classe

Dans l'extrait de code suivant, l'élément Color est déclaré dans l'enum Car afin de limiter la portée de l'énumération et de ne pas "polluer" l'espace de noms global.

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) Est-ce une bonne façon de limiter le champ d'action de la Color enum ? Ou bien, dois-je le déclarer en dehors de l'enum Car mais éventuellement dans son propre espace de noms ou sa propre structure ? Je suis tombé sur cet article aujourd'hui, qui préconise cette dernière solution et aborde quelques points intéressants concernant les enums : http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) Dans cet exemple, lorsque l'on travaille sur la classe, est-il préférable de coder l'enum en tant que Car::Color ou simplement Color (Je suppose que la première solution est la meilleure, au cas où il y aurait une autre solution). Color déclarée dans l'espace de noms global. De cette façon, au moins, nous sommes explicites quant à l'enum auquel nous nous référons).

101voto

Peter Alexander Points 31990
  1. Si Color est quelque chose qui est spécifique à juste Car alors c'est de cette façon que vous limiterez sa portée. Si vous voulez avoir un autre Color que d'autres classes utilisent, autant la rendre globale (ou au moins en dehors de l'espace de travail). Car ).

  2. Cela ne fait aucune différence. S'il existe une fonction globale, la fonction locale est utilisée de toute façon car elle est plus proche de la portée actuelle. Notez que si vous définissez ces fonctions en dehors de la définition de la classe, vous devrez explicitement spécifier Car::Color dans l'interface de la fonction.

12 votes

2. Oui et non. Car::Color getColor() mais void Car::setColor(Color c) parce que dans setColor nous avons déjà le spécificateur.

95voto

Andreas Florath Points 1958

Aujourd'hui - avec C++11 - vous pouvez utiliser enum classe pour ça :

enum class Color { RED, BLUE, WHITE };

AFAII cela fait exactement ce que vous voulez.

4 votes

Malheureusement, il n'autorise pas les fonctions membres : stackoverflow.com/a/53284026/7395227

77voto

Sergey Teplyakov Points 6556

Je préfère l'approche suivante (code ci-dessous). Elle résout le problème de la "pollution de l'espace de noms", mais elle est aussi beaucoup plus sûre (vous ne pouvez pas assigner et même comparer deux énumérations différentes, ou votre énumération avec d'autres types intégrés, etc.)

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

Utilisation :

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

Je crée des macros pour faciliter l'utilisation :

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

Utilisation :

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

Quelques références :

  1. Herb Sutter, Jum Hyslop, C/C++ Users Journal, 22(5), mai 2004.
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Strongly Typed Enums (révision 3), juillet 2007

0 votes

Cela me plaît. Cela oblige également l'enum à être instancié avec une valeur valide. Je pense qu'un opérateur d'affectation et un constructeur de copie seraient utiles. De plus, t_ devrait être privé. Les macros, je peux m'en passer.

0 votes

Moi aussi, j'aime bien ça. Merci pour les références.

1 votes

Vous avez dit : "Il est également beaucoup plus sûr (vous ne pouvez pas assigner et même comparer deux énumérations différentes ..." . Pourquoi pensez-vous que c'est une bonne fonction ? Je pense que if(c2 == Color::Red ) est raisonnable et doit compiler, mais dans votre exemple, ce n'est pas le cas. Même argument pour l'affectation aussi !

7voto

Matthieu M. Points 101624

En général, je place toujours mes enums dans un fichier struct . J'ai vu plusieurs directives, dont celle du "préfixe".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

J'ai toujours pensé que ça ressemblait plus à C que C++ (d'une part, à cause de l'abréviation et, d'autre part, à cause des espaces de noms dans le domaine de la santé). C++ ).

Ainsi, pour limiter le champ d'application, nous avons maintenant deux alternatives :

  • espaces de noms
  • structs/classes

Personnellement, j'ai tendance à utiliser un struct car il peut être utilisé comme paramètre pour la programmation de modèles, alors qu'un espace de nom ne peut pas être manipulé.

Voici quelques exemples de manipulation :

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

qui retourne le nombre d'éléments de l'enum à l'intérieur de la struct T :)

3voto

Harvey Points 1039

Si vous créez une bibliothèque de code, alors j'utiliserais l'espace de noms. Cependant, vous ne pouvez avoir qu'un seul enum Color dans cet espace de noms. Si vous avez besoin d'un enum qui pourrait utiliser un nom commun, mais qui pourrait avoir des constantes différentes pour différentes classes, utilisez votre approche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X