Оголошення перерахунку в класі


151

У наступному фрагменті коду Colorenum оголошується в межах 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в глобальному просторі імен буде оголошено ще один перелік. Таким чином, принаймні, ми чітко заявляємо про перерахування, на яке ми посилаємося.)

Відповіді:


86
  1. Якщо Colorце щось специфічне тільки для Cars, то саме так ви обмежили б його сферу застосування. Якщо у вас буде інший Colorперелік, який використовують інші класи, то ви також можете зробити його глобальним (або принаймні зовні Car).

  2. Це не має ніякої різниці. Якщо існує глобальний, то локальний все одно використовується, оскільки він ближче до нинішньої області. Зауважте, що якщо ви визначаєте ці функції поза визначенням класу, вам потрібно буде чітко вказати Car::Colorв інтерфейсі функції.


12
2. Так і ні. Car::Color getColor()а void Car::setColor(Color c)тому, що у setColorнас вже є специфікатор.
Маттьє М.


66

Я віддаю перевагу наступному підходу (код нижче). Він вирішує проблему "забруднення простору імен", але також набагато безпечніший (ви не можете призначити і навіть порівнювати два різні перерахування, або ваше перерахування з будь-якими іншими вбудованими типами тощо).

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. Herb Sutter, Jum Hyslop, C / C ++ Journal Journal, 22 (5), травень 2004 року
  2. Герб Саттер, Девід Е. Міллер, Bjarne Stroustrup сильно набрали переліків (редакція 3), липень 2007 р.

Мені подобається це. Це також примушує екземпляр передбачити дійсне значення. Я думаю, що оператор призначення та конструктор копій були б корисні. Також t_ має бути приватним. Макроси, без яких я можу.
jmucchiello

Мені теж це подобається. Дякую за довідку.
anio

1
Ви сказали: "також це набагато більш безпечно типу (ви не можете призначити і навіть порівнювати два різні перерахування ..." . Чому ви вважаєте, що це хороша функція? Я думаю, що if(c2 == Color::Red )це розумно і потрібно компілювати, але у вашому прикладі це Це ж аргумент для присвоєння!
Наваз

3
@Nawaz c2іншого типу ( Color2), тож чому, на вашу думку, c2 == Color::Redі завдання повинні складатися? Що робити, якщо Color::Redце 1, а Color2::Redце 2? Чи варто Color::Red == Color2::Redоцінювати до trueчи false? Якщо ви змішаєте неписемні перелічувачі, вам буде погано провести час.
Віктор К

2
Чому не тип t_; приватний?
Zingam

7

Взагалі, я завжди вкладаю свої перерахунки в struct. Я бачив кілька вказівок, серед яких "префіксація".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

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

Тож для обмеження сфери застосування у нас є дві альтернативи:

  • простори імен
  • структури / заняття

Я особисто схильний до використання, structоскільки його можна використовувати як параметри для програмування шаблонів, тоді як простором імен не можна маніпулювати.

Приклади маніпуляцій включають:

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

який повертає кількість елементів перерахунку всередині структури T:)


3

Якщо ви створюєте бібліотеку кодів, я б використовував простір імен. Однак все одно у цьому просторі імен може бути лише один перелік кольорів. Якщо вам потрібен перелічник, який може використовувати загальну назву, але може мати різні константи для різних класів, використовуйте свій підхід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.