C ++有两种 enum :
enum
enum class
以下是如何声明它们的几个示例:
enum class Color { red, green, blue }; // enum class enum Animal { dog, cat, bird, human }; // plain enum
的 两个有什么区别? 强>
enum class es - 枚举器名称是 的 本地 强> 对于枚举和他们的价值观 不 隐式转换为其他类型(如另一种类型) enum 要么 int )
int
川 enum s - 枚举器名称与枚举及其对应的范围相同 值隐式转换为整数和其他类型
例:
enum Color { red, green, blue }; // plain enum enum Card { red_card, green_card, yellow_card }; // another plain enum enum class Animal { dog, deer, cat, bird, human }; // enum class enum class Mammal { kangaroo, deer, human }; // another enum class void fun() { // examples of bad use of plain enums: Color color = Color::red; Card card = Card::green_card; int num = color; // no problem if (color == Card::red_card) // no problem (bad) cout << "bad" << endl; if (card == Color::green) // no problem (bad) cout << "bad" << endl; // examples of good use of enum classes (safe) Animal a = Animal::deer; Mammal m = Mammal::deer; int num2 = a; // error if (m == a) // error (good) cout << "bad" << endl; if (a == Mammal::deer) // error (good) cout << "bad" << endl; }
enum class es应该是首选,因为它们可以减少可能导致错误的意外。
C ++ 11常见问题解答 提到以下几点:
的 常规枚举隐式转换为int,当有人不希望枚举充当整数时会导致错误。 强>
enum color { Red, Green, Yellow }; enum class NewColor { Red_1, Green_1, Yellow_1 }; int main() { //! Implicit conversion is possible int i = Red; //! Need enum class name followed by access specifier. Ex: NewColor::Red_1 int j = Red_1; // error C2065: 'Red_1': undeclared identifier //! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1; int k = NewColor::Red_1; // error C2440: 'initializing': cannot convert from 'NewColor' to 'int' return 0; }
的 常规枚举将其枚举器导出到周围的范围,导致名称冲突。 强>
// Header.h enum vehicle { Car, Bus, Bike, Autorickshow }; enum FourWheeler { Car, // error C2365: 'Car': redefinition; previous definition was 'enumerator' SmallBus }; enum class Editor { vim, eclipes, VisualStudio }; enum class CppEditor { eclipes, // No error of redefinitions VisualStudio, // No error of redefinitions QtCreator };
的 无法指定枚举的基础类型,从而导致混淆,兼容性问题,并且无法进行前向声明。 强>
// Header1.h #include <iostream> using namespace std; enum class Port : unsigned char; // Forward declare class MyClass { public: void PrintPort(enum class Port p); }; void MyClass::PrintPort(enum class Port p) { cout << (int)p << endl; }
。
// Header.h enum class Port : unsigned char // Declare enum type explicitly { PORT_1 = 0x01, PORT_2 = 0x02, PORT_3 = 0x04 };
// Source.cpp #include "Header1.h" #include "Header.h" using namespace std; int main() { MyClass m; m.PrintPort(Port::PORT_1); return 0; }
从 Bjarne Stroustrup的C ++ 11常见问题解答 :
该 enum class es(“new enums”,“strong enums”)解决了三个问题 使用传统的C ++枚举: 常规枚举隐式转换为int,当有人不希望枚举充当整数时会导致错误。 常规枚举将其枚举器导出到周围的范围,导致名称冲突。 一个基础类型 enum 无法指定,造成混淆,兼容性问题,并作出前瞻性声明 不可能。 新的枚举是“枚举类”,因为它们将传统枚举(名称值)的各个方面与类的方面(作用域成员和缺少转换)结合起来。
该 enum class es(“new enums”,“strong enums”)解决了三个问题 使用传统的C ++枚举:
新的枚举是“枚举类”,因为它们将传统枚举(名称值)的各个方面与类的方面(作用域成员和缺少转换)结合起来。
因此,正如其他用户所提到的,“强大的枚举”会使代码更安全。
“经典”的基本类型 enum 应该是一个足够大的整数类型,以适应所有的值 enum ;这通常是一个 int 。每个枚举类型也应兼容 char 或有符号/无符号整数类型。
char
这是对什么的广泛描述 enum 底层类型必须是,因此每个编译器将自己决定经典的基础类型 enum 有时结果可能会令人惊讶。
例如,我已经看过这样的代码了很多次:
enum E_MY_FAVOURITE_FRUITS { E_APPLE = 0x01, E_WATERMELON = 0x02, E_COCONUT = 0x04, E_STRAWBERRY = 0x08, E_CHERRY = 0x10, E_PINEAPPLE = 0x20, E_BANANA = 0x40, E_MANGO = 0x80, E_MY_FAVOURITE_FRUITS_FORCE8 = 0xFF // 'Force' 8bits, how can you tell? };
在上面的代码中,一些天真的编码器认为编译器会存储 E_MY_FAVOURITE_FRUITS 值为无符号8位类型...但不保证它:编译器可以选择 unsigned char 要么 int 要么 short ,这些类型中的任何一种都足够大,以适应所见的所有值 enum 。添加字段 E_MY_FAVOURITE_FRUITS_FORCE8 是一种负担,并不会强迫编译器对基础类型做出任何选择 enum 。
E_MY_FAVOURITE_FRUITS
unsigned char
short
E_MY_FAVOURITE_FRUITS_FORCE8
如果有一些代码依赖于类型大小和/或假设 E_MY_FAVOURITE_FRUITS 将具有某种宽度(例如:序列化例程),这些代码可能会以一些奇怪的方式表现,具体取决于编译器的想法。
更糟糕的是,如果一些同事不经意地为我们的人增加了新的价值 enum :
E_DEVIL_FRUIT = 0x100, // New fruit, with value greater than 8bits
编译器不会抱怨它!它只是调整类型的大小以适应所有的值 enum (假设编译器使用的是最小的类型,这是我们不能做的假设)。这种简单而粗心的补充 enum 可能会细微地打破相关代码。
由于C ++ 11可以指定基础类型 enum 和 enum class (谢谢 RDB 所以这个问题得到了很好的解决:
enum class E_MY_FAVOURITE_FRUITS : unsigned char { E_APPLE = 0x01, E_WATERMELON = 0x02, E_COCONUT = 0x04, E_STRAWBERRY = 0x08, E_CHERRY = 0x10, E_PINEAPPLE = 0x20, E_BANANA = 0x40, E_MANGO = 0x80, E_DEVIL_FRUIT = 0x100, // Warning!: constant value truncated };
如果字段的表达式超出此类型的范围,则指定基础类型,编译器将抱怨而不是更改基础类型。
我认为这是一个很好的安全改进。
所以 为什么枚举类优于普通枚举? ,如果我们可以选择范围的基础类型( enum class )和无范围( enum )枚举其他什么 enum class 更好的选择?:
枚举用于表示一组整数值。
该 class 之后的关键字 enum 指定枚举是强类型的,其枚举数是作用域的。这条路 enum 类防止意外误用常量。
class
例如:
enum class Animal{Dog, Cat, Tiger}; enum class Pets{Dog, Parrot};
在这里,我们不能混合动物和宠物的价值观。
Animal a = Dog; // Error: which DOG? Animal a = Pets::Dog // Pets::Dog is not an Animal
因为,正如在其他答案中所说,类枚举不能隐式转换为int / bool,它也有助于避免错误的代码,如:
enum MyEnum { Value1, Value2, }; ... if (var == Value1 || Value2) // Should be "var == Value2" no error/warning