首页 > 编程笔记 > C++笔记 阅读:165

C++枚举类型和枚举类的用法(非常详细)

除了常见的数值数据和文字数据外,在现实世界中常常遇到这样的数据:
这些数据都只有有限的几种可能值,且值也只能是这个范围中的某一个。为了抽象和表达这种特殊数据,C++ 提供了枚举(enum)机制。

使用 C++ 的枚举机制,可以通过列举某种数据的所有可能取值来定义一种新的数据类型,这种数据类型通常被称为枚举类型。当使用枚举类型来定义变量时,该变量的取值就限定在枚举类型所列举的可能取值范围内。

定义枚举类型的语法格式如下:
enum 枚举类型名
{
    //可能的枚举值列表
};  //注意,这里必须以分号表示结束
其中,enum 是定义枚举类型的关键字;枚举类型名是要创建的新数据类型的名称。完成枚举类型的定义后,我们可以将它作为数据类型来定义变量。在枚举类型的定义中,可以逐个列出该枚举类型的所有可能值。

例如,可以将描述交通灯颜色的数据抽象成一个枚举类型:
//交通灯颜色
enum TrafficLight
{
    RED,   //红
    GERRN, //绿
    YELLOW //黄
};
有了这样的 TrafficLight 枚举类型的定义,我们就可以将它作为数据类型来定义一个变量,用以表示交通灯的颜色:
// 定义一个变量light表示交通灯的颜色
// 将枚举值RED赋值给它,表示当前交通灯是红色的
TrafficLight light = RED;
因为枚举类型所表达数据的特殊性,所以我们在应用枚举类型时需要注意以下几点。

1) 枚举类型中的枚举值有对应的默认整数值

本质上,枚举类型的数据是整型数值,每个枚举类型的可选值其实是一个整数值。默认情况下,第一个枚举值对应的是整数值为 0,第二个枚举值对应的是整数值是 1,以此类推。例如,上面 TrafficLight 枚举类型中的 RED 可选值其实就是整数值 0,而 GREEN 对应 1,YELLOW 自然就对应 2。

如果认为某些枚举值默认的整数值不合适,例如,在某些情况下,我们希望某个枚举值拥有特殊的整数值来表示特殊含义,那么可以在定义时单独指定各个枚举值对应的整数值。例如:
//单独指定各个枚举值对应的整数值
enum TrafficLight
{
    RED =1,     //指定对应的整数值,不再是从0开始
    GERRN,      //2
    YELLOW = 0  //指定对应的整数值为0,表示特殊含义
}
经过人为地指定枚举值对应的数值后,RED 对应的整数值变为1,其后的 GREEN 增加 1,变为 2。对于最后的 YELLOW,因为其特殊意义,人为指定其整数值为 0。

2) 枚举类型变量的赋值

如果变量是枚举类型,那么只能使用该枚举类型中的某个枚举值对其进行赋值。例如:
//红灯
TrafficLight light = RED;
//变为绿灯
light = GREEN;
//如果使用枚举值外的数据对其进行赋值,就会导致编译错误
//也就是使这个值是某个枚举值对应的整数值
light = 1;
换句话说,如果希望把某个变量的取值范围限定在几个可选值之内,则可以把该变量定义为枚举类型变量。

3) 枚举值是常量,定义后不可改变其对应的整数值

在定义枚举类型时,已经确定了枚举值及其对应的整数值。这些整数值要么是默认值,要么是指定的特殊数值。一旦定义完成,各个枚举值的整数值就成为常量,必须按照常量对待,不能再对其进行赋值或修改。也就是说,在完成定义后,不能改变任何枚举值对应的整数值。

例如,下面的语句是错误的:
RED = 4;  // 尝试改变枚举值,导致编译错误
枚举类型实际上定义了一组整型常量。在上面的例子中,我们可以使用 3 个整型常量来表示交通信号灯的 3 种颜色。然而,枚举类型允许我们使用描述性的名称来表示有限范围内的整数值,而不是直接使用含义不明确的整数值。这种做法有助于确保变量接受合法且易于理解的预期值,使得代码含义明确,更具可读性,也更易于维护。因此,当需要表达的数据“只有有限的几种可能值”时,我们应该优先选择使用枚举类型。

带作用域的枚举类

虽然枚举类型可以方便地定义某个范围内的枚举值,但由于传统的枚举类型本质上是一个 int 类型的整数,因此在使用中常常会遇到各种各样的问题。

例如,传统枚举类型的所有枚举值在其定义后的整个代码范围内都可以使用,而这可能会造成名字污染或冲突,使得后续代码无法再将该枚举值名称用作他途。此外,传统枚举类型无法指定底层数据类型,这可能会造成内存资源的浪费,并且使得枚举类型无法进行前向声明。

所谓前向声明,是指在某个元素(函数或者类)尚未定义时,为了提前使用它而进行的声明,向编译器表明源文件中将会有这个元素的定义,从而可以安心使用,具体的定义会稍后提供。

为了解决传统枚举类型存在的这些问题,C++ 的最新标准引入了新的作用域枚举类,它允许指定底层数据类型。定义作用域枚举类的语法形式与定义传统枚举类型的语法形式十分相似:
enum class枚举类名:数据类型
{
  // 可能的枚举值列表
};
枚举类的定义以 enum class 开始,后面跟着枚举类名,其后可以用冒号:指定枚举类的底层数据类型。枚举类的底层数据类型必须是有符号或无符号的整型数,如果不指定,其默认的数据类型是 int 类型。例如:
//定义一个枚举类 TrafficLight
//并指定其底层数据类型为 char 类型
enum class TrafficLight : char
{
    RED = 1, //红
    GERRN,   //绿
    YELLOW   //黄
};
虽然枚举类的定义与传统的枚举类型的定义十分相似,但由于两者内在机制的不同,它们已经是两个完全不同的概念。枚举类具有作用域,其枚举值只在其作用域内可见,这有效地解决了可能由枚举值引起的名字污染问题。例如:
//枚举值 RED 属于 TrafficLight 作用域
//所以我们必须在其前面加上TrafficLight才能访问
TrafficLight light = TrafficLight::RED;
//定义一个名为 RED 的变量,虽然与枚举值 RED 同名
//但是两者不会产生冲突,也就是枚举值RED的定义不会引起名字污染
bool RED = true;
枚举类除解决名字污染的问题外,还有另一个显著的优势:它允许开发者指定底层数据类型,这使得前向声明成为可能。此外,根据枚举值的数量选择合适的底层数据类型,也可以在一定程度上避免资源浪费。例如:
//使用前向声明,只是声明了枚举类的名字
//没有定义具体的枚举值
enum class TrafficLight : char; //使用前向声明的枚举类
void foo(TrafficLight* light)
{
    //...
}
//...
//补充完枚举类的定义
enum class TrafficLight : char
{
    RED=1,//红色
    //...
};
在这段代码中,完成了枚举类 TrafficLight 的前向声明后,就可以直接使用它了。最后,只需要补充具体的定义即可。另外,在这里指定了枚举值的底层数据类型为 char,这比默认的 int 更加节省资源。

从上面的例子可以看出,枚举类的使用有效地解决了传统枚举类型在使用中遇到的各种问题,所以,在今后的编程实践中,我们应该优先选择使用枚举类。

相关文章