Mendeklarasikan enum di dalam kelas

150

Dalam cuplikan kode berikut, Colorenum dideklarasikan di dalam Carkelas untuk membatasi ruang lingkup enum dan berusaha untuk tidak "mencemari" namespace 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) Apakah ini cara yang baik untuk membatasi ruang lingkup Colorenum? Atau, haruskah saya mendeklarasikannya di luar Carkelas, tetapi mungkin di dalam namespace atau struct sendiri? Saya baru saja menemukan artikel ini hari ini, yang mendukung yang terakhir dan membahas beberapa poin bagus tentang enum: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) Dalam contoh ini, ketika bekerja di dalam kelas, apakah yang terbaik untuk kode enum sebagai Car::Color, atau hanya Colorcukup? (Saya menganggap yang pertama lebih baik, kalau-kalau ada Colorenum lain yang dideklarasikan di namespace global. Dengan begitu, setidaknya, kami secara eksplisit tentang enum yang kami rujuk.)

bporter
sumber

Jawaban:

85
  1. Jika Colorada sesuatu yang khusus untuk just Carmaka itu adalah cara Anda akan membatasi ruang lingkupnya. Jika Anda akan memiliki Colorenum lain yang digunakan kelas lain maka Anda mungkin membuatnya global (atau setidaknya di luar Car).

  2. Tidak ada bedanya. Jika ada yang global maka yang lokal masih digunakan karena lebih dekat dengan ruang lingkup saat ini. Perhatikan bahwa jika Anda mendefinisikan fungsi-fungsi di luar definisi kelas maka Anda harus menentukan secara eksplisit Car::Colordi antarmuka fungsi.

Peter Alexander
sumber
12
2. Ya dan tidak. Car::Color getColor()tetapi void Car::setColor(Color c)karena di setColorkita sudah memiliki specifier.
Matthieu M.
84

Saat ini - menggunakan C ++ 11 - Anda dapat menggunakan kelas enum untuk ini:

enum class Color { RED, BLUE, WHITE };

AFAII ini melakukan apa yang Anda inginkan.

Andreas Florath
sumber
2
Sayangnya, itu tidak memungkinkan fungsi anggota: stackoverflow.com/a/53284026/7395227
Andreas
66

Saya lebih suka pendekatan berikut (kode di bawah). Ini memecahkan masalah "polusi namespace", tetapi juga jauh lebih aman untuk mengetik (Anda tidak dapat menetapkan dan bahkan membandingkan dua enumerasi yang berbeda, atau enumerasi Anda dengan tipe bawaan lainnya, dll.).

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

Pemakaian:

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

Saya membuat makro untuk memfasilitasi penggunaan:

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

Pemakaian:

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

Beberapa referensi:

  1. Herb Sutter, Jum Hyslop, C / C ++ Users Journal, 22 (5), Mei 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Sangat Ketik Enums (revisi 3), Juli 2007
Sergey Teplyakov
sumber
Saya suka ini. Ini juga memaksa enum untuk dipakai dengan nilai yang valid. Saya pikir operator penugasan dan copy constructor akan berguna. Juga t_ harus pribadi. Makro yang bisa saya lakukan tanpanya.
jmucchiello
Saya juga suka ini. Terima kasih untuk referensi.
anio
1
Anda berkata: "juga jauh lebih aman untuk mengetik (Anda tidak dapat menetapkan dan bahkan membandingkan dua enumerasi yang berbeda ..." . Menurut Anda mengapa ini adalah fitur yang baik? Saya pikir if(c2 == Color::Red )ini wajar dan harus dikompilasi, tetapi dalam contoh Anda itu tidak. Argumen yang sama untuk penugasan juga!
Nawaz
3
@Nawaz c2adalah tipe lain ( Color2), jadi mengapa menurut Anda c2 == Color::Reddan tugas harus dikompilasi? Bagaimana jika Color::Red1, dan Color2::Red2? Haruskah Color::Red == Color2::Redmengevaluasi ke trueatau false? Jika Anda mencampur enumerator yang tidak aman, Anda akan memiliki waktu yang buruk.
Victor K
2
Mengapa tidak Ketikkan t_; pribadi?
Zingam
7

Secara umum, saya selalu memasukkan enum ke dalam struct. Saya telah melihat beberapa pedoman termasuk "awalan".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Selalu berpikir ini lebih mirip Cpedoman daripada C++yang (untuk satu karena singkatan dan juga karena ruang nama di C++).

Jadi untuk membatasi ruang lingkup kita sekarang memiliki dua alternatif:

  • ruang nama
  • struct / kelas

Saya pribadi cenderung menggunakan a structkarena ini dapat digunakan sebagai parameter untuk pemrograman template sementara namespace tidak dapat dimanipulasi.

Contoh manipulasi meliputi:

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

yang mengembalikan jumlah elemen enum di dalam struct T:)

Matthieu M.
sumber
3

Jika Anda membuat perpustakaan kode, maka saya akan menggunakan namespace. Namun, Anda masih dapat hanya memiliki satu Warna enum di dalam namespace itu. Jika Anda memerlukan enum yang mungkin menggunakan nama umum, tetapi mungkin memiliki konstanta berbeda untuk kelas yang berbeda, gunakan pendekatan Anda.

Harvey
sumber