# 【CCG】枚举
作者:wallace-lai
发布:2024-02-25
更新:2024-02-25
枚举通常用于定义整数值集以及定义此类值集的类型,C++中有两种枚举,分别是普通的enum和enum class。
## 规则1:优选枚举而不是宏定义
宏定义和枚举相比,缺少作用域和类型方面的限制。宏定义在预处理阶段就会被处理掉,不会出现在诸如调试器这样的工具当中。
```cpp
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
#define RED 0
#define PURPLE 1
#define BLUE 2
int webby = BLUE; // webby == 2; probably not what was desired
```
以上代码显示了使用宏定义的一大缺陷:**在不同头文件中定义相同名称的宏可能导致你引用出错**。相反,如果你使用enum class,这样的问题就不会出现。
```cpp
enum class WebColor {
red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF
};
enum class ProductInfo {
red = 0,
purple = 1,
blue = 2
};
int webby = blue; // error : be specific
WebColor webby = WebColor::blue;
```
## 注:C++中的enum和enum class
在C++中,`enum`和`enum class`都是用于定义枚举类型的关键字,但它们之间存在一些重要的区别。
1. **作用域**:`enum`定义的枚举类型中的枚举值具有**全局作用域**,而`enum class`定义的枚举类型中的枚举值具有**局部作用域**。也就是说,如果你使用`enum`定义了一个枚举类型,那么你可以在任何地方直接使用枚举值,而如果你使用`enum class`定义了一个枚举类型,那么你必须使用枚举类型名作为前缀来引用枚举值。
例如:
```cpp
enum Color { RED, GREEN, BLUE };
enum class ColorClass { RED, GREEN, BLUE };
// 使用enum
Color myColor = RED;
// 使用enum class
ColorClass myColorClass = ColorClass::RED;
```
2. **类型转换**:`enum`定义的枚举类型与整数类型之间可以进行隐式转换,而`enum class`定义的枚举类型与整数类型之间则不能进行隐式转换,只能进行显式转换。这可以防止因类型不匹配而导致的错误。
例如:
```cpp
enum Color { RED, GREEN, BLUE };
enum class ColorClass { RED, GREEN, BLUE };
Color color = RED;
int intColor = color; // 隐式转换,这是可以的
ColorClass colorClass = ColorClass::RED;
int intColorClass = colorClass; // 错误:不能进行隐式转换
int intColorClassCorrect = static_cast(colorClass); // 正确:进行显式转换
```
**注:限制隐式类型转换而允许你显示转换,目的是让你脑子里对转换过程有根弦,避免在你感知不到的地方发生错误**!
3. **枚举值的默认类型**:`enum`定义的枚举值的默认类型是`int`,而`enum class`定义的枚举值的默认类型是枚举类型本身。
4. **枚举值的范围**:`enum`定义的枚举值的范围与`int`相同,而`enum class`定义的枚举值的范围则与底层类型相同,可以通过指定底层类型来改变枚举值的范围。
例如:
```cpp
enum Color : char { RED, GREEN, BLUE }; // 枚举值的范围与char相同
```
总的来说,`enum`和`enum class`各有其优点和适用场景。`enum`在C++的早期版本中使用较多,但由于其作用域和类型转换的特性可能导致一些问题,**因此在现代的C++代码中,更推荐使用`enum class`来定义枚举类型**。
## 规则2:使用枚举来表示相关的命名常量
使用枚举来表示相应的命名常量后,在使用switch语句的过程中,编译器可以自动检查出关于case分支的一些异常情况。
```cpp
enum class ProductInfo {
red = 0,
purple = 1,
blue = 2
};
void print(ProductInfo info)
{
switch (info) {
// 使用enum class使得编译器可以检查一些不寻常的case分支
switch (info) {
case ProductInfo::red :
cout << "red" << endl;
break;
case ProductInfo::purple :
cout << "purple" << endl;
break;
}
}
return;
}
```
比如对于上面的代码,编译器将给出如下的警告。这样就可以在编译过程中发现一些潜在的错误。
```
warning: enumeration value ‘blue’ not handled in switch [-Wswitch]
```
## 规则3:优选enum class而不是enum
具体原因在enum和enum class之间的不同这小节中已经说明了。概括来说是enum class在类型转换和作用域方面比enum有更加严格的限制,更加安全。
## 规则4:为了更加安全和方便,请在枚举之上定义操作函数
如下所示,请将操作定义在枚举之上(**前提是它适合定义成枚举**):
```cpp
enum class Day {
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
Sun
};
Day &operator++(Day &d)
{
return d = (d == Day::Sun) ? Day::Mon :
static_cast(static_cast(d) + 1);
}
```
注意上面的代码,尽管多次使用static_cast让代码变得不够美观,但是如果不使用则会得到一个错误。
```cpp
Day &operator++(Day &d)
{
return d = (d == Day::Sun) ? Day::Mon :
Day{++d}; // error
}
```
## 规则5:禁止在枚举中使用全大写定义
```cpp
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
enum class Product_info { RED, PURPLE, BLUE }; // syntax error
```
这条规则的目的主要是**为了避免和宏定义产生命名冲突**,因为宏定义是使用全大写的形式,所以禁止枚举也使用全大写的形式。
## 规则6:避免使用匿名枚举
如果你不对枚举进行命名,那么枚举值就无法和名字关联起来。尽量避免下面这种匿名枚举类型的出现。
```cpp
// bad example
enum {
red = 0xFF0000,
scale = 4,
is_signed = 1
};
```
如果无法避免,那么请考虑使用`constexpr`进行替代:
```cpp
constexpr int red = 0xFF0000;
constexpr short scale = 4;
constexpr bool is_signed = true;
```
## 规则7:只在需要时指定枚举值类型
C++中enum枚举值的默认类型是int,int是最方便用于读写的类型,这和C语言也是兼容的。只在有特殊需要的时候指定枚举值类型,除此之外一律使用默认的int类型。
```cpp
// 为了节省内存空间
enum class Direction : char {
n, s, e, w,
ne, nw, se, sw
};
// 多余的类型指定,默认就是int32_t
enum class WebColor : int32_t {
red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF,
};
```
注意:在以下这种前置声明的场景下需要指定enum的类型
```cpp
enum Flags : char;
void f(Flags);
// ...
enum Flags : char {
// ...
}
```
## 规则8:只在需要时指定枚举的具体值
```cpp
enum class Col1 {
red,
yellow,
blue
};
enum class Col2 {
red = 1,
yellow = 2,
blue = 2
};
```
和C语言一样,不指定的情况下,C++从0开始连续不重复地分配枚举值。因此,只在有特殊需要的时候才需要指定枚举的具体值。