枚举

我之前讲了一些整数类型,如 int long long 等等,它们的取值范围都比较大。比如 int 可以取到数十亿的范围,long long 则高达 101810^{18}。即便是最小的 char,取值范围也有几百。(布尔类型暂且不考虑。)但在一些场合,某个值的取值范围可能只有数十个甚至几个,那么采用过大取值范围的数据类型来表示它就不太合适了。

比如我想描述一个小人目前正在前进的方向。它可以前进的方向只有东、南、西、北四种。如果用 int 来表示这个数据,那么就需要规定 0 表示东、1 表示西……,然后还需要规定大于 3 的值是错误的。这中额外的编码工作会让代码看上去更费劲了。此时我们可以选用枚举类型作为一个更合适的数据类型来表示它。

枚举类型(Enumeration)是规定了一个有限取值范围的数据类型。它的所有可能取值称为这个类型的枚举项(Enumerator)。声明枚举类型的语法是这样的:

enum 枚举类型名 :  {
    枚举项列表
};

先抛开 : 不管;枚举项列表 是若干个逗号分隔的名字,表示这个类型的所有可能取值。刚刚小人前进方向的例子中,如果用枚举类型来表示它的话就写成这样:

enum Direction {
    East,
    South,
    West,
    North
};

这段代码的意思是,声明一个枚举类型 Direction,它的取值范围只能是 East South WestNorth。下面给出了枚举类型的使用例子:

// 声明并定义一个枚举
Direction d{East};
// 赋值
d = North;
// 判断
if (d == West) {
    cout << "He is going to west." << endl;
}
// d = 3; // 编译错误:不能将 int 类型变量赋值给 Direction 类型

需要注意的是,这种声明不仅引入了类型名 Direction,还向全局命名空间引入了四个枚举项(East South WestNorth)的名字,从而你能在任何地方使用这些枚举项。如果不希望这种名字的污染,可以使用带作用域的枚举类型:

enum classstruct 枚举类型名 :  {
    枚举项列表
}

enum 关键字后加上 class 或者 struct,这个枚举便成为了带有作用域的枚举类型。当枚举类型带有作用域时,其枚举项需要带 枚举类型名:: 前缀使用:

enum class Direction {
    East,
    South,
    West,
    North
};
Direction d{Direction::East};
d = Direction::North;
// d = North; // 编译错误:North 未定义

我们推荐在 C++ 中使用带有作用域的枚举,这样能有助于避免命名冲突。

枚举类型的底层实现

关于枚举类型,我们还需要了解其底层是如何实现的。对于一个枚举类型来说,其实际上定义了若干个整数类型常量作为其枚举项。这些常量的值是从 0 开始的连续整数。比如在最初的 Direction 例子中,编译器定义了四个常量 East South WestNorth,它们的取值分别为 0 1 2 3。也就是说,

Direction d{East};
d = North;

会被编译器翻译成

int d{0};
d = 3;

正因为枚举项底层实现是整数类型,所以 C++ 允许从枚举项到整数类型的转换。(但反过来不行,如果反过来就违背了枚举类型“有限个取值”的初衷。)

int a{North}; // 即 int a{3};

你可以手动指定一个枚举项底层的值。

enum Mode {
    Execute = 1,
    Write = 2,
    Read = 4
};

在这个例子中,枚举 Mode 的三个枚举项以 = 常量 的后缀的形式给出了对应枚举项底层的值。所以,此时如果写下 int a{Read}; 则代表了 int a{4};

如果没有显式给出一个枚举项底层的值,则它的值为上一个枚举项的取值 +1。第一个枚举项底层的值默认为 0。

枚举项底层的类型被称为这个枚举类型的基类型(Enum base type),简称。默认一个枚举的基是 int,所以在上面所有例子中见到的枚举或枚举项都会被翻译成 int 类型的变量或值的操作。当然,你可以更改一个枚举的基,方法就是指定最初提到的 :

enum class Direction: short {
    East,
    South,
    West,
    North
};
int main() {
    Direction d{Direction::East};
    sizeof(d); // sizeof(d) 的值等于 sizeof(short)
}

更改枚举的基会影响枚举类型的变量所占用的存储空间,以及其所能取到的枚举项的最大个数。

最近更新:
代码未运行