C++语言|enum那些事
引入概念
当我们需要表示如状态(文件的打开、关闭、读取、写入)、颜色(红色、绿色、蓝色)或对象类型(圆形、矩形、三角形)等场景时,枚举类型是一个不错的选择。
而什么是枚举类型呢?从上图的使用场景也可直窥一二。
定义一个传统的枚举类型:
1 | enum color { red, orange, blue, yellow}; // red = 0, orange = 1, blue = 2, yellow = 3 |
从上面的示例,可以得出简单的结论:枚举(Enumeration)是一种用于定义一组具有离散取值的相关命名常量的数据类型。
上面定义了一个传统枚举类型,也就意味着 C++ 是有新的枚举类型的,C++11 中引入了枚举类(enum class)。至于为什么有了传统的枚举类型还要定义新的枚举类呢?下面来探究一下。
传统枚举
如上面所举出的例子,枚举的语法定义格式为:
enum 枚举名 { 枚举成员1, 枚举成员2, ……}
枚举的使用:枚举名 枚举变量 = 枚举元素;
通过枚举类型的语法格式和使用,就可以进行简单的枚举使用。
1 | enum day { MON, TUE, WED, THU, FRI, SAT, SUN}; |
既然可以使用,又为什么需要产生新枚举类呢?这就不得不提传统枚举的缺陷。
- 命名冲突
枚举值放置在同一作用域中时,两个枚举定义中的枚举量可能产生冲突。
1 | enum Color { RED, GREEN, BLUE}; |
在这个示例中定义了两个不同的枚举类型:Color
和 Fruit
。然而它们都包含一个名为 RED
的枚举成员。当在同一个作用域内引用 RED
,编译器将无法区分是 Color RED
还是 Fruit RED
,由此编译器将产生错误。
- 类型转换
传统 C++ 枚举类型的枚举值被视为整数类型,因此它们可以隐式地转换为整数,这可能导致类型安全性问题,尤其是当不同枚举类型之间的值可以互相赋值时。
1 | enum Color { RED, GREEN, BLUE}; |
在这个示例中,当我们将 Color
定义的枚举变量赋值给整型变量时,会发生枚举值的隐式转换。
- 全局作用域
传统 C++ 枚举类型的枚举值在整个作用域内可见,这可能导致不希望的全局命名污染问题,尤其是在不同的代码模块中。当然这和命名冲突是一个问题,但在这里我们更关注在不同代码模块中。
1 | // 文件A.cpp |
因为传统枚举类型的枚举值在整个作用域内可见,所以在文件 B.cpp
中引入 Direction
枚举时,它也包含了 UP
,导致与文件 B.cpp
中定义的 State
枚举的 ON
值冲突。这就是全局命名污染问题,因为不同的枚举类型之间可能会产生意外的命名冲突,使代码难以理解和维护。
- 无法限制枚举的有效值
传统的 C++ 枚举类型无法限制变量接受的值范围,因为它们的枚举成员只是整数常量的命名表示,可以赋予枚举变量任何整数值,即使这些值不在枚举成员的范围内。
1 | enum Month { JANUARY = 1, FEBRUARY, MARCH, ... , DECEMBER}; |
这种情况可能导致在程序中使用无效的枚举值,因为编译器不会阻止将任何整数值赋给枚举变量,这限制了类型安全性。
新枚举类(C++11)
C++11 引入了一种新的枚举类型,称为”枚举类”(enum class),也被称为”强类型枚举”。枚举类是传统枚举类型的改进版本,它提供了更好的类型安全性和作用域控制。
新枚举类的语法定义格式为:
enum class name { 枚举成员1, 枚举成员2, …… };
以下是一个枚举类的示例:
1 | enum class Color { RED, GREEN, BLUE }; |
可以看到新的枚举类作用域不再是全局的了,必须使用枚举名来限定枚举量。此外新枚举类不能隐式地转换成其它类型,但可以通过显式来转换。
另外新枚举类还提供了一种语法:
1 | enum class pizza : short { Small, Medium, Large }; |
:short
将底层类型指定为 short ,底层类型必须为整型。