enum和enum class
一、 enum
(传统枚举 / Unscoped Enum)
enum
是 C 语言就有的特性,在 C++ 中也得到了支持。它定义了一组具名的整数常量。
特点:
作用域污染 (Scope Pollution):
enum
的枚举器(即具名常量)会泄露到定义它们的命名空间或全局作用域中。这意味着你不能在同一个作用域中定义同名的枚举器,即使它们属于不同的enum
类型。1
2
3
4
5
6enum Color { RED, GREEN, BLUE };
enum TrafficLight { RED_LIGHT, YELLOW_LIGHT, GREEN_LIGHT }; // 编译错误:RED_LIGHT 与 Color::RED 冲突 (如果 RED_LIGHT 也叫 RED)
// 实际上这里不会冲突,因为名字不同。但如果 TrafficLight 也有一个叫 RED 的枚举器,就会冲突。
// 假设:
// enum Color { RED, GREEN, BLUE };
// enum Feeling { HAPPY, SAD, RED }; // 编译错误:RED 重定义隐式类型转换 (Implicit Conversion):
enum
的枚举器可以隐式转换为整数类型(如int
),也可以隐式地与其他整数类型进行比较或算术运算。这可能导致类型不安全的问题。1
2
3
4
5
6
7
8
9
10enum Color { RED, GREEN, BLUE };
Color c = RED;
int i = c; // 隐式转换为 int,i = 0,合法
if (c == 0) { // 隐式比较,合法
// ...
}
Color another_c = BLUE;
if (c == another_c) { // 合法
// ...
}底层类型不确定 (Underlying Type): 编译器会为
enum
选择一个合适的整数类型来存储枚举值(例如int
、char
等),这个类型是实现定义的,通常是能够容纳所有枚举器值的最小整数类型。你也可以显式指定底层类型(C++11 引入),但这在传统enum
中不常见。1
2enum Color { RED, GREEN, BLUE };
// sizeof(Color) 可能是 4 (int)默认值: 如果没有显式赋值,第一个枚举器的值为
0
,后续枚举器的值在前一个的基础上递增1
。1
2
3
4
5
6enum Status {
OK, // 0
ERROR, // 1
WARNING = 5, // 5
FATAL // 6
};
示例:
1 |
|
二、 enum class
(枚举类 / Scoped Enum) (C++11)
enum class
是 C++11 引入的,旨在解决传统 enum
的主要缺点:作用域污染和类型不安全。
特点:
强作用域 (Strongly Scoped):
enum class
的枚举器只在其枚举类型的作用域内可见。你需要使用EnumTypeName::EnumeratorName
的方式来引用它们。这完全消除了作用域污染问题。1
2
3
4
5
6enum class Color { RED, GREEN, BLUE };
enum class TrafficLight { RED, YELLOW, GREEN }; // 合法!两个 RED 不冲突
Color c = Color::RED; // 必须使用作用域限定符
// TrafficLight tl = RED; // 编译错误:RED 未定义
TrafficLight tl = TrafficLight::RED; // 合法强类型 (Strongly Typed):
enum class
的枚举器不会隐式转换为整数类型。你也不能将整数隐式赋值给enum class
类型。这大大提高了类型安全性,避免了许多潜在的错误。1
2
3
4
5
6
7
8
9
10
11
12
13
14enum class Color { RED, GREEN, BLUE };
Color c = Color::RED;
// int i = c; // 编译错误:不能隐式转换为 int
int i = static_cast<int>(c); // 必须显式转换
// if (c == 0) { // 编译错误:不能隐式比较
// // ...
// }
if (c == Color::RED) { // 合法,同类型比较
// ...
}
// Color another_c = TrafficLight::RED; // 编译错误:不同枚举类型不能直接比较或赋值默认底层类型为
int
: 如果不显式指定,enum class
的底层类型默认为int
。你可以显式指定底层类型,这在传统enum
中也是可行的,但对于enum class
来说,这种控制更为重要。1
2enum class Color : unsigned char { RED, GREEN, BLUE }; // 显式指定底层类型为 unsigned char
// sizeof(Color) 会是 1
示例:
1 |
|
三、 总结与选择
特性 | enum (传统枚举) |
enum class (枚举类 / 作用域枚举) |
---|---|---|
C++版本 | C++98 及更高版本 | C++11 及更高版本 |
作用域 | 枚举器泄露到父作用域,可能导致命名冲突 | 强作用域,枚举器只在枚举类型内部可见 |
引用方式 | EnumeratorName 或 EnumTypeName::EnumeratorName |
必须 EnumTypeName::EnumeratorName |
类型安全性 | 弱类型,可隐式转换为整数,可与整数比较 | 强类型,不能隐式转换为整数,不能与整数比较 |
底层类型 | 编译器决定 (实现定义),默认为 int |
默认为 int ,可显式指定 |
隐式转换 | enum -> int (是) |
enum class -> int (否,需要 static_cast ) |
不同枚举类型比较 | 如果底层值相同,可能隐式比较 (不安全) | 否,编译错误 |
主要优点 | 兼容 C 语言,在某些遗留代码中可能更方便 | 避免命名冲突,提高类型安全性,代码更清晰 |
主要缺点 | 作用域污染,类型不安全 | 需要显式作用域限定符和类型转换 |
何时选择哪种?
- 推荐使用
enum class
: 在现代 C++ 编程中,几乎所有情况下都应该优先使用enum class
。它解决了传统enum
的主要痛点,提供了更好的类型安全性和代码清晰度,减少了潜在的错误。 - 使用
enum
的场景:- 与 C 语言代码兼容: 如果你需要与 C 语言代码进行接口交互,并且 C 代码期望接收或返回传统的
enum
类型,那么可能需要使用enum
。 - 作为位标志 (Bit Flags): 传统
enum
的隐式转换为整数的特性,有时在作为位标志使用时会显得“方便”(尽管仍然不安全)。然而,即使是位标志,也可以通过重载运算符等方式让enum class
也能很好地工作,并且更安全。
- 与 C 语言代码兼容: 如果你需要与 C 语言代码进行接口交互,并且 C 代码期望接收或返回传统的