Deklarowanie wyliczenia w klasie


150

W poniższym fragmencie kodu Colorwyliczenie jest zadeklarowane w Carklasie, aby ograniczyć zakres wyliczenia i nie „zanieczyszczać” globalnej przestrzeni nazw.

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) Czy to dobry sposób na ograniczenie zakresu Colorwyliczenia? A może powinienem zadeklarować go poza Carklasą, ale prawdopodobnie w jego własnej przestrzeni nazw lub strukturze? Właśnie dziś trafiłem na ten artykuł, który opowiada się za tym drugim i omawia kilka fajnych punktów na temat wyliczeń: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) W tym przykładzie, podczas pracy w klasie, czy najlepiej jest zakodować wyliczenie jako Car::Color, czy po prostu Colorwystarczy? (Zakładam, że to pierwsze jest lepsze, na wypadek gdyby Colorw globalnej przestrzeni nazw zadeklarowano inne wyliczenie. W ten sposób przynajmniej jasno określamy wyliczenie, do którego się odnosimy).

Odpowiedzi:


85
  1. Jeśli Colorjest to coś, co jest specyficzne dla samego Cars, to w ten sposób można ograniczyć jego zakres. Jeśli zamierzasz mieć inne Colorwyliczenie, którego używają inne klasy, równie dobrze możesz ustawić je jako globalne (lub przynajmniej na zewnątrz Car).

  2. To nie robi różnicy. Jeśli istnieje globalny, lokalny jest nadal używany, ponieważ jest bliżej bieżącego zakresu. Zauważ, że jeśli zdefiniujesz te funkcje poza definicją klasy, będziesz musiał jawnie określić Car::Colorw interfejsie funkcji.


12
2. Tak i nie. Car::Color getColor()ale void Car::setColor(Color c)ponieważ setColormamy już specyfikator.
Matthieu M.


66

Wolę następujące podejście (kod poniżej). Rozwiązuje problem "zanieczyszczenia przestrzeni nazw", ale jest też dużo bardziej bezpieczny dla typów (nie możesz przypisać, a nawet porównać dwóch różnych wyliczeń lub wyliczenia z innymi wbudowanymi typami itp.).

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

Stosowanie:

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

Tworzę makro w celu ułatwienia użytkowania:

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

Stosowanie:

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

Niektóre odniesienia:

  1. Herb Sutter, Jum Hyslop, C / C ++ Users Journal, 22 (5), maj 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Strongly Typed Enums (rewizja 3), lipiec 2007

Lubię to. Wymusza również utworzenie wystąpienia wyliczenia z prawidłową wartością. Myślę, że operator przypisania i konstruktor kopiujący byłyby przydatne. Również t_ powinno być prywatne. Makra, bez których mogę się obejść.
jmucchiello

Ja też to lubię. Dzięki za referencje.
anio,

1
Powiedziałeś: „także jest znacznie bardziej bezpieczny dla typów (nie możesz przypisać, a nawet porównać dwóch różnych wyliczeń ...”) . Dlaczego uważasz, że to dobra funkcja? Myślę, że if(c2 == Color::Red )jest rozsądna i musi się skompilować, ale w twoim przykładzie jest to nie. Ten sam argument dla przypisania również!
Nawaz

3
@Nawaz c2jest innego typu ( Color2), więc jak myślisz, dlaczego c2 == Color::Redprzypisania powinny się kompilować? A jeśli Color::Redjest 1, a Color2::Redto 2? Powinien Color::Red == Color2::Redocenić truelub false? Jeśli pomieszasz niezabezpieczone typy wyliczające, będziesz się źle bawić.
Victor K

2
Dlaczego nie jest Type t_; prywatny?
Zingam

7

Ogólnie rzecz biorąc, zawsze umieszczam wyliczenia w pliku struct. Widziałem kilka wskazówek, w tym „prefiksowanie”.

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Zawsze uważałem, że wygląda to bardziej jak Cwytyczne niż C++jedne (na przykład ze względu na skrót, a także ze względu na przestrzenie nazw w C++).

Aby ograniczyć zakres, mamy teraz dwie alternatywy:

  • przestrzenie nazw
  • struktury / klasy

Osobiście zwykle używam a, structponieważ może być używany jako parametr do programowania szablonów, podczas gdy przestrzenią nazw nie można manipulować.

Przykłady manipulacji obejmują:

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

która zwraca liczbę elementów enum wewnątrz struktury T:)


3

Jeśli tworzysz bibliotekę kodu, użyłbym przestrzeni nazw. Jednak w tej przestrzeni nazw nadal można mieć tylko jedno wyliczenie koloru. Jeśli potrzebujesz wyliczenia, które może używać wspólnej nazwy, ale może mieć różne stałe dla różnych klas, użyj swojego podejścia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.