2010-03-23 18 views
114

W poniższym fragmencie kodu wyliczenie Color jest zadeklarowane w klasie Car w celu ograniczenia zakresu wyliczenia i próby "zanieczyszczenia" globalnej przestrzeni nazw.Zgłaszanie wyliczenia w klasie

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, aby ograniczyć zakres Color wyliczenia? Czy powinienem zadeklarować go poza klasą Car, ale być może w obrębie własnej przestrzeni nazw lub struktury? Właśnie natknąłem się na ten artykuł dzisiaj, który opowiada się za tym ostatnim i omawia kilka fajnych punktów dotyczących wyliczeń: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums.

(2) W tym przykładzie, gdy pracuje ciągu klasę, jest to najlepiej kodować enum jako Car::Color, lub po prostu Color wystarczyć? (Zakładam, że ta pierwsza jest lepsza, na wypadek gdyby w globalnej przestrzeni nazw zostało zadeklarowane inne wyliczenie Color. W ten sposób, jednoznacznie mówimy o wyliczeniu, do którego się odnosimy.)

Odpowiedz

69
  1. Jeśli Color jest coś, co jest charakterystyczne dla zaledwie Car s wówczas, że jest sposób, by ograniczyć jego zakres. Jeśli masz zamiar mieć inne wyliczenie, którego używają inne klasy, możesz równie dobrze zrobić to globalnie (lub przynajmniej poza Car).

  2. Nie ma znaczenia. Jeśli istnieje globalna, to i tak lokalna jest nadal używana, ponieważ jest bliższa bieżącemu zakresowi. Zauważ, że jeśli zdefiniujesz te funkcje poza definicją klasy, będziesz musiał wyraźnie określić Car::Color w interfejsie funkcji.

+10

2. Tak i nie. 'Car :: Color getColor()' ale 'void Car :: setColor (Color c)' ponieważ w 'setColor' mamy już specyfikator. –

1

Jeśli tworzysz kod biblioteka, wtedy użyłbym przestrzeni nazw. W tym obszarze nazw można jednak nadal zawierać tylko jedną enum kolorystyczną. 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.

6

Ogólnie rzecz biorąc, zawsze umieszczam moje wyrazy w struct. Widziałem kilka wytycznych, w tym "prefixing".

enum Color 
{ 
    Clr_Red, 
    Clr_Yellow, 
    Clr_Blue, 
}; 

Zawsze uważałem ten wyglądał bardziej niż C++C wytyczne te (dla jednej ze względu na skrót, a także ze względu na przestrzenie nazw w C++).

więc ograniczenie zakresu mamy teraz dwie możliwości:

  • nazw
  • elemencie/klas

osobiście tendencję do korzystania z struct ponieważ może on być stosowany jako parametry szablonu programowanie, podczas gdy przestrzeni nazw nie można manipulować.

Przykładami manipulacji są:

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

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

58

wolę następujące podejście (kod poniżej). Rozwiązuje problem "zanieczyszczenia przestrzeni nazw", ale także jest o wiele bardziej bezpieczny (nie można 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; 
}; 

Zastosowanie:

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 

utworzyć makro, aby ułatwić korzystanie z:

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

wykorzystania:

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

Niektóre odnośniki pomocny:

  1. Herb Sutter, Jum Hyslop, C/C++ Users Journal, 22 (5), maj 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup silnie typami wyliczenia (wersja 3), Lipiec 2007
+0

Podoba mi się to. Zmusza ono także do utworzenia instancji z poprawną wartością. Sądzę, że użyteczny byłby operator przydziału i konstruktor kopii. Również t_ powinno być prywatne. Makra, z którymi mogę się obejść. – jmucchiello

+0

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

+1

Powiedziałeś: * "również jest o wiele bardziej bezpieczny (nie możesz przypisać, a nawet porównać dwóch różnych wyliczeń ..." *) Dlaczego uważasz, że jest to dobra funkcja? Myślę, że 'if (c2 == Kolor: : Czerwony) 'jest uzasadnione i musi się kompilować, ale w twoim przykładzie nie ma tego samego argumentu dla przypisania również! – Nawaz

66

Obecnie - przy użyciu C++ 11 - można użyć enum class na to:

enum class Color { RED, BLUE, WHITE }; 

AFAII to robi dokładnie to, co chcesz.