Объявление перечисления в классе - PullRequest
136 голосов
/ 24 марта 2010

В следующем фрагменте кода перечисление Color объявлено в классе Car, чтобы ограничить область действия перечисления и попытаться не «загрязнять» глобальное пространство имен.

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) Это хороший способ ограничить область действия перечисления Color? Или я должен объявить его вне класса Car, но, возможно, в его собственном пространстве имен или структуре? Я только что наткнулся на эту статью сегодня, которая защищает последнюю и обсуждает некоторые приятные моменты о перечислениях: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums.

(2) В этом примере при работе в классе лучше всего кодировать перечисление как Car::Color, или достаточно просто Color? (Я предполагаю, что первое лучше, на случай, если в глобальном пространстве имен будет объявлено другое перечисление Color. Таким образом, по крайней мере, мы явно говорим о перечислении, на которое ссылаемся.)

Ответы [ 5 ]

78 голосов
/ 12 октября 2012

В настоящее время - используя C ++ 11 - вы можете использовать enum class для этого:

enum class Color { RED, BLUE, WHITE };

AFAII, это именно то, что вы хотите.

78 голосов
/ 24 марта 2010
  1. Если Color - это нечто, характерное только для Car s, тогда вы ограничите его область действия. Если у вас будет другое перечисление Color, которое используют другие классы, вы также можете сделать его глобальным (или, по крайней мере, вне Car).

  2. Это не имеет значения. Если есть глобальный, то локальный все равно используется, так как он ближе к текущей области. Обратите внимание, что если вы определяете эти функции вне определения класса, вам нужно явно указать Car::Color в интерфейсе функции.

62 голосов
/ 24 марта 2010

Я предпочитаю следующий подход (код ниже). Он решает проблему «загрязнения пространства имен», но он также намного более безопасен для типов (вы не можете назначать и даже сравнивать два различных перечисления или ваше перечисление с любыми другими встроенными типами и т. Д.).

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

Использование:

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

Я создаю макрос для облегчения использования:

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

Использование:

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

Некоторые ссылки:

  1. Херб Саттер, Jum Hyslop, C / C ++ Users Journal, 22 (5), май 2004 г.
  2. Херб Саттер, Дэвид Э. Миллер, Бьярн Страуструп, сильно типизированные перечисления (редакция 3), июль 2007 г.
7 голосов
/ 24 марта 2010

В общем, я всегда помещаю свои перечисления в struct. Я видел несколько рекомендаций, включая «префикс».

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Всегда думал, что это больше похоже на C рекомендации, чем C++ (для одного из-за сокращения, а также из-за пространств имен в C++).

Итак, чтобы ограничить область действия, у нас теперь есть две альтернативы:

  • пространств имен
  • Структуры / классы

Лично я склонен использовать struct, потому что его можно использовать в качестве параметров для программирования шаблонов, а пространством имен нельзя манипулировать.

Примеры манипуляций включают в себя:

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

, который возвращает количество элементов enum внутри структуры T:)

3 голосов
/ 24 марта 2010

Если вы создаете библиотеку кода, я бы использовал пространство имен. Тем не менее, вы можете иметь только одно перечисление Color внутри этого пространства имен. Если вам нужен enum, который может использовать общее имя, но может иметь разные константы для разных классов, используйте ваш подход.

...