引入概念

当我们需要表示如状态(文件的打开、关闭、读取、写入)、颜色(红色、绿色、蓝色)或对象类型(圆形、矩形、三角形)等场景时,枚举类型是一个不错的选择。

而什么是枚举类型呢?从上图的使用场景也可直窥一二。

定义一个传统的枚举类型:

1
2
3
4
5
enum color { red, orange, blue, yellow}; // red = 0, orange = 1, blue = 2, yellow = 3
color book = red;
color pencil = blue;
std::cout << book << std::endl; // 0
std::cout << pencil << std::endl; // 2

从上面的示例,可以得出简单的结论:枚举(Enumeration)是一种用于定义一组具有离散取值的相关命名常量的数据类型。

上面定义了一个传统枚举类型,也就意味着 C++ 是有新的枚举类型的,C++11 中引入了枚举类(enum class)。至于为什么有了传统的枚举类型还要定义新的枚举类呢?下面来探究一下。

传统枚举

如上面所举出的例子,枚举的语法定义格式为:

enum 枚举名 { 枚举成员1, 枚举成员2, ……}

枚举的使用:枚举名 枚举变量 = 枚举元素;

通过枚举类型的语法格式和使用,就可以进行简单的枚举使用。

1
2
enum day { MON, TUE, WED, THU, FRI, SAT, SUN};
day today = WED;

既然可以使用,又为什么需要产生新枚举类呢?这就不得不提传统枚举的缺陷。

  1. 命名冲突

枚举值放置在同一作用域中时,两个枚举定义中的枚举量可能产生冲突。

1
2
enum Color { RED, GREEN, BLUE};
enum Fruit { APPLE, BANANA, ORANGE, RED};

在这个示例中定义了两个不同的枚举类型:ColorFruit。然而它们都包含一个名为 RED 的枚举成员。当在同一个作用域内引用 RED ,编译器将无法区分是 Color RED 还是 Fruit RED ,由此编译器将产生错误。

  1. 类型转换

传统 C++ 枚举类型的枚举值被视为整数类型,因此它们可以隐式地转换为整数,这可能导致类型安全性问题,尤其是当不同枚举类型之间的值可以互相赋值时。

1
2
3
4
enum Color { RED, GREEN, BLUE};

Color myColor = RED;
int colorValue = myColor; // 枚举值隐式转换为整数

在这个示例中,当我们将 Color 定义的枚举变量赋值给整型变量时,会发生枚举值的隐式转换。

  1. 全局作用域

传统 C++ 枚举类型的枚举值在整个作用域内可见,这可能导致不希望的全局命名污染问题,尤其是在不同的代码模块中。当然这和命名冲突是一个问题,但在这里我们更关注在不同代码模块中。

1
2
3
4
5
6
7
8
9
10
// 文件A.cpp
enum Direction { UP, DOWN, LEFT, RIGHT};

// 文件B.cpp
enum State { UN, OFF};

// 在文件B.cpp中引入了文件A.cpp中定义的Direction枚举值
// 这可能导致不希望的全局命名污染
std::cout << UP << std::endl; // 输出: 0
std::cout << UP << std::endl; // 输出: 0

因为传统枚举类型的枚举值在整个作用域内可见,所以在文件 B.cpp 中引入 Direction 枚举时,它也包含了 UP ,导致与文件 B.cpp 中定义的 State 枚举的 ON 值冲突。这就是全局命名污染问题,因为不同的枚举类型之间可能会产生意外的命名冲突,使代码难以理解和维护。

  1. 无法限制枚举的有效值
    传统的 C++ 枚举类型无法限制变量接受的值范围,因为它们的枚举成员只是整数常量的命名表示,可以赋予枚举变量任何整数值,即使这些值不在枚举成员的范围内。
1
2
3
4
enum Month { JANUARY = 1, FEBRUARY, MARCH, ... , DECEMBER};

Month currentMonth = static_cast<Month>(42); // 赋予一个超出范围的整数值
// currentMonth 现在包含了一个不在枚举范围内的值

这种情况可能导致在程序中使用无效的枚举值,因为编译器不会阻止将任何整数值赋给枚举变量,这限制了类型安全性。

新枚举类(C++11)

C++11 引入了一种新的枚举类型,称为”枚举类”(enum class),也被称为”强类型枚举”。枚举类是传统枚举类型的改进版本,它提供了更好的类型安全性和作用域控制。

新枚举类的语法定义格式为:

enum class name { 枚举成员1, 枚举成员2, …… };

以下是一个枚举类的示例:

1
2
3
4
5
6
7
enum class Color { RED, GREEN, BLUE };

int main() {
Color myColor = Color::RED;
// myColor = 1; // error, 不能将整数值直接赋给枚举类变量
int colorValue = static_cast<int>(myColor); // 显式转换为整数
}

可以看到新的枚举类作用域不再是全局的了,必须使用枚举名来限定枚举量。此外新枚举类不能隐式地转换成其它类型,但可以通过显式来转换。

另外新枚举类还提供了一种语法:

1
enum class pizza : short { Small, Medium, Large };

:short 将底层类型指定为 short ,底层类型必须为整型。