第一次學習枚舉類型時,以爲這個名字很詭異。可是後來發現,「枚舉」真的特別傳神,枚舉就是可數的意思。html
當你發現某個類型的值是數得過來的,那就派枚舉出場吧。c++
C++11是個大版本,必定程度上從新定義了C++,其中就包括新增的emum class。追本溯源,咱們先看老枚舉。程序員
enum Color { WHITE=1, BLACK=2 };
若是咱們的代碼定義了兩個不一樣的枚舉類型,而它們的枚舉值取了相同的名字,那麼編譯器將報錯。編程
enum Color { WHITE=1, BLACK=2 }; enum Species { BLACK=1, WHITE=2, YELLOW=3 };
這種錯誤在如下狀況很容易發生安全
因而公司爲了減小此類錯誤,可能制定以下編程規範:函數
全部的枚舉,值的名字都要以該枚舉名爲前綴工具
因而Species定義以下,是否是略感繁瑣呢?佈局
enum Species { SPECIES_BLACK = 1, SPECIES_WHITE = 2, SPECIES_YELLOW = 3 };
名字衝突會在編譯期暴露,可是類型問題卻會躲過編譯器的審查,致使難以追蹤的邏輯Bug,更爲可怕。老式枚舉很大程度上只是個整數類型。學習
枚舉能夠直接和數字比較指針
enum Color { COLOR_WHITE=1, COLOR_BLACK=2 }; if (COLOR_WHITE == 1) { // do something }
不一樣的枚舉也能夠比較
enum Color { COLOR_WHITE=1, COLOR_BLACK=2 }; enum Species { SPECIES_BLACK=1, SPECIES_WHITE=2, SPECIES_YELLOW=3 }; if (COLOR_WHITE == SPECIES_BLACK) { // do something }
不要覺得程序員不會犯這樣的錯誤,當被產品經理的需求踢着屁股跑時,他們可什麼事情都幹得出來。
前向聲明是隱藏實現和減小代碼依賴的有利工具。可是下面的代碼會編譯報錯。
// forward declaration enum Color; class A { public: void foo(Color c); private: // some members .. };
C++的編譯過程實際上是內存佈局的過程。當咱們前向聲明class時,會使用指針或引用,內存大小固定是8個字節(64位機器)。但這裏使用的是Color自己,而枚舉的大小實際上是由實現決定的。
enum Color { COLOR_WHITE = 1, COLOR_BLACK = 2 }; // sizeof(Color) == 4 enum Color { COLOR_WHITE = 100000000000, COLOR_BLACK = 2 }; // sizeof(Color) == 8
看到沒有,通常狀況下枚舉的大小是4字節,當枚舉值太大時,就會變成8字節。因此編譯器並不能僅靠前向聲明下判斷。
C++11提出了enum class,也稱爲strong typed enum,它解決了老式枚舉的三個問題
enum class Color { WHITE=1, BLACK=2 }; enum class Species { BLACK=1. WHITE=2, YELLOW=3, BROWN=4 }; // 使用時,枚舉類型名爲上層命名空間 // Color::WHITE // Color::BLACK // Species::BLACK // Species::YELLOW
枚舉不能直接和數字比較
// 編譯報錯 if (Color::WHITE == 1) { // do something }
不一樣的枚舉不能比較
// 編譯報錯 if (Color::WHITE == Species::WHITE) { // do something }
enum class 能夠指定底層的實現,因此編譯器就知道枚舉的大小了。
// 成功編譯,哈哈 // 還能夠指定 int, unsigned int, short等,整數類型均可以 enum class Color : char; class A { public: void foo(Color c); }; enum class Color : char { WHITE=1, BLACK=2 }; // sizeof(Color) == 1
雖然enum class作了不少改進,可是並不完美,仍是有不少值得注意的地方。
新枚舉不是class,而是一個單獨的類型,更像是整數類型的封裝。它沒有像類同樣的構造和析構機制。若是定義一個未指定值的枚舉,那麼默認值是0,即便定義中沒有0
enum class Color { WHITE=1, BLACK=2 }; Color c; static_cast<int>(c); // it's 0
這是我怨念的地方。有時候我想把枚舉以字符串的形式打印出來,可讀性更好。若是能像下面同樣寫代碼多好,惋惜enum class不支持定義任何方法。
Color c = Color::WHITE // 不可能如此調用 c.toString() // "white"
因而只能用單獨的函數實現了,代碼的組織不夠緊湊,也沒夠美觀。
const char* ColorToString(Color c) { switch (c) { case Color::WHITE: return "white"; case Color::BLACK: return "black"; default: abort(); // 多是未初始化的枚舉,這屬於邏輯錯誤 } }