为什么枚举类优先于普通枚举?
我听到一些人build议在C ++中使用枚举类 ,因为它们的types安全 。
但是这到底意味着什么呢?
C ++有两种enum
:
-
enum class
es - 平原
enum
以下是一些如何声明它们的例子:
enum class Color { red, green, blue }; // enum class enum Animal { dog, cat, bird, human }; // plain enum
两者有什么区别?
-
enum class
es – 枚举器名称是枚举的本地和它们的值不隐式转换为其他types(如另一个enum
或int
) -
Plain
enum
s – 枚举器名称与枚举的范围相同,并且它们的值隐式转换为整数和其他types
例:
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应该是首选,因为它们导致更less的可能导致错误的意外。
来自Bjarne Stroustrup的C ++ 11 FAQ :
enum class
es(“new enums”,“strong enums”)解决了传统C ++枚举的三个问题:
- 常规枚举隐式转换为int,导致错误,当有人不希望枚举作为一个整数。
- 常规枚举将其枚举器导出到周围的范围,导致名称冲突。
- 不能指定
enum
的基础types,导致混淆,兼容性问题,并使前向声明变得不可能。新枚举是“枚举类”,因为它们将传统枚举(名称值)的方面与类(范围成员和缺乏转换)的方面结合在一起。
所以,正如其他用户所说,“强枚举”会使代码更安全。
“经典” enum
的基础types应该是一个足够大的整数types,以适应enum
所有值; 这通常是一个int
。 同样,每个枚举types应该与char
或有符号/无符号整数types兼容。
这是一个enum
基础types必须的广泛描述,所以每个编译器都会自己对经典enum
的基本types进行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位types…但是没有任何保证:编译器可以selectunsigned char
或int
或short
,其中任何types都是大的足以适应在enum
看到的所有值。 添加字段E_MY_FAVOURITE_FRUITS_FORCE8
是一个负担,并且不会强制编译器对enum
的基础types做出任何select。
如果有一些代码依赖于types大小和/或者假设E_MY_FAVOURITE_FRUITS
会有一定的宽度(例如:序列化例程),那么这个代码可能会以一些奇怪的方式运行,这取决于编译器的想法。
而更糟糕的是,如果一些同事不小心增加了我们的enum
的新价值:
E_DEVIL_FRUIT = 0x100, // New fruit, with value greater than 8bits
编译器不会抱怨! 它只是调整types以适应enum
所有值(假定编译器使用的是可能的最小types,这是我们不能做的假设)。 这个简单和粗心的除了可以微妙的破解相关的代码。
因为C ++ 11可以指定enum
和enum class
的基础types(感谢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 };
如果某个字段的expression式超出此types的范围,则指定基础types,编译器将会投诉,而不是更改基础types。
我认为这是一个很好的安全改进。
编辑
那么为什么枚举类比普通枚举首选呢? ,如果我们可以selectscoped( enum class
)和unscoped( enum
)枚举的基础types,还有什么使enum class
更好的select?
- 它们不会隐式转换为
int
。 - 他们不污染周围的名字空间。
- 他们可以被提前宣布。
使用枚举类超过正常枚举的基本优点是,你可能有两个不同的枚举相同的枚举variables,仍然可以解决它们(这已被提到OP types安全 )
例如:
enum class Color1 { red, green, blue }; //this will compile enum class Color2 { red, green, blue }; enum Color1 { red, green, blue }; //this will not compile enum Color2 { red, green, blue };
至于基本枚举,编译器将无法区分red
是否指向Color1
或Color2
types,如下面的语句所示。
enum Color1 { red, green, blue }; enum Color2 { red, green, blue }; int x = red; //Compile time error(which red are you refering to??)
枚举用于表示一组整数值。
enum
之后的class
关键字指定枚举types是强types的,枚举types是作用域的。 这样, enum
类可以防止意外滥用常量。
例如:
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