第一次学习枚举类型时,以为这个名字很诡异。可是后来发现,“枚举”真的特别传神,枚举就是可数的意思。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(); // 多是未初始化的枚举,这属于逻辑错误 } }