C++枚举类型enum与enum class的使用 the enum class (also called a scoped enumeration) 枚举定义将被限制在枚举作用域内
C++枚举类型enum与enum class的使用
C++枚举类型enum与enum class的使用_C 语言_脚本之家 https://www.jb51.net/article/193758.htm
一.关于枚举类型
1. 什么是枚举类型?
答:如果一个变量只有几种可能的值,那么就可以定义为枚举类型,比如:性别只有男和女,那么就可以将性别定义为一种枚举类型,其中男和女就是性别所包含的变量。所谓”枚举”是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。在C++中,枚举类型分为不限定作用域(enum)和限定作用域(enum class)。
2. enum与enum class的区别? (为什么需要限定作用域?)
答:枚举作用域是指枚举类型成员名字的作用域,起自其声明之处,终止枚举定义结束之处。enum与class enum区别在于是否限定其作用域。C语言规定,枚举类型(enum)的成员的可见范围被提升至该枚举类型所在的作用域内。这被认为有可能污染了外部的作用域,为此,C++11引入了枚举类(enum class)解决此问题。
举个例子:
通常情况下,我们定义一个枚举类型:
1
2
3
4
5
|
enum Sex { Girl, Boy }; |
这时,你不可以再使用一个枚举去定义Girl 和 Boy了,如:
1
2
3
4
5
6
|
// 错误,编译器提示 Girl,Boy重定义 enum Student { Girl, Boy }; |
编译错误的原因在于Sex与Student都处在同一作用域下,成员变量重定义。
这便体现C++11引入枚举类(enum class)的重要性,enum class能够有效对枚举作用域进行限定,避免了枚举成员的重定义。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
enum class Sex { Girl, Boy }; enum class Student { Girl, Boy }; int main( int argc, char *argv[]) { Sex a = Sex::Gril; Student b = Student::Gril; //两者处于不同作用域下,不会重定义 } |
二.枚举类型enum的使用
定义:
1
2
3
4
5
6
7
8
9
10
11
12
|
enum /*枚举类型*/ { /*枚举成员*/ , /*枚举成员*/ , /*枚举成员*/ }; 如: enum Sex { Girl, Boy }; |
声明:
1
|
Sex a=Girl; |
注意:
1.枚举中每个成员(标识符)结束符是“,”而不是”;” 最后一个成员可省略”,”
2.初始化时可以赋负数,以后的标识符仍依次加1。
3.枚举变量只能取枚举说明结构中的某个标识符常量。
4.在外部可以对枚举变量进行赋值,但需要进行类型转换。
5.枚举常数可以隐式转换为int,但是int不可以隐式转换为枚举值。
6.为枚举中的每个名称分配一个整数值,该值与其在枚举中的顺序相对应。默认情况下,第一个值分配0,下一个值分配1,依次类推,但也可以显示设置枚举名称的值。
7.枚举值可以用来作判断比较。
三.枚举类型enum class的使用
定义:
1
2
3
4
5
6
|
enum class /*枚举类型*/ { /*枚举成员*/ , /*枚举成员*/ , /*枚举成员*/ }; |
如:
1
2
3
4
5
|
enum class Sex { Girl, Boy }; |
声明:
1
|
Sex a=Sex::Gril; |
枚举定义将被限制在枚举作用域内,并且不能隐式转换为整数类型,但是可以显式转化为整数类型,如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
enum class Sex { Girl, Boy }; int main( int argc, char *argv[]) { Sex a=Sex::Gril; int d1 =a; // 错误,无法从“Girl”隐式转换为“int”。 int d2 = int (a); // 正确,显示将enum class转换为整数 return 0; } |
8.3 — Enum classes
Although enumerated types are distinct types in C++, they are not type safe, and in some cases will allow you to do things that don’t make sense. Consider the following case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <iostream>
int main()
{
enum Color
{
color_red, // color_red is placed in the same scope as Color
color_blue
};
enum Fruit
{
fruit_banana, // fruit_banana is placed in the same scope as Fruit
fruit_apple
};
Color color{ color_red }; // Color and color_red can be accessed in the same scope (no prefix needed)
Fruit fruit{ fruit_banana }; // Fruit and fruit_banana can be accessed in the same scope (no prefix needed)
if (color == fruit) // The compiler will compare a and b as integers
std::cout << "color and fruit are equal\n"; // and find they are equal!
else
std::cout << "color and fruit are not equal\n";
return 0;
}
|
When C++ compares color and fruit, it implicitly converts color and fruit to integers, and compares the integers. Since color and fruit have both been set to enumerators that evaluate to value 0, this means that in the above example, color will equal fruit. This is definitely not as desired since color and fruit are from different enumerations and are not intended to be comparable! With standard enumerators, there’s no way to prevent comparing enumerators from different enumerations.
C++11 defines a new concept, the enum class (also called a scoped enumeration), which makes enumerations both strongly typed and strongly scoped. To make an enum class, we use the keyword class after the enum keyword. Here’s an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <iostream>
int main()
{
enum class Color // "enum class" defines this as a scoped enumeration instead of a standard enumeration
{
red, // red is inside the scope of Color
blue
};
enum class Fruit
{
banana, // banana is inside the scope of Fruit
apple
};
Color color{ Color::red }; // note: red is not directly accessible any more, we have to use Color::red
Fruit fruit{ Fruit::banana }; // note: banana is not directly accessible any more, we have to use Fruit::banana
if (color == fruit) // compile error here, as the compiler doesn't know how to compare different types Color and Fruit
std::cout << "color and fruit are equal\n";
else
std::cout << "color and fruit are not equal\n";
return 0;
}
|
With normal enumerations, enumerators are placed in the same scope as the enumeration itself, so you can typically access enumerators directly (e.g. red). However, with enum classes, the strong scoping rules mean that all enumerators are considered part of the enumeration, so you have to use a scope qualifier to access the enumerator (e.g. Color::red). This helps keep name pollution and the potential for name conflicts down.
Because the enumerators are part of the enum class, there’s no need to prefix the enumerator names (e.g. it’s okay to name them “red” instead of “color_red”, since Color::color_red is redundant).
The strong typing rules means that each enum class is considered a unique type. This means that the compiler will not implicitly compare enumerators from different enumerations. If you try to do so, the compiler will throw an error, as shown in the example above.
However, note that you can still compare enumerators from within the same enum class (since they are of the same type):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
int main()
{
enum class Color
{
red,
blue
};
Color color{ Color::red };
if (color == Color::red) // this is okay
std::cout << "The color is red!\n";
else if (color == Color::blue)
std::cout << "The color is blue!\n";
return 0;
}
|
With enum classes, the compiler will no longer implicitly convert enumerator values to integers. This is mostly a good thing. However, there are occasionally cases where it is useful to be able to do so. In these cases, you can explicitly convert an enum class enumerator to an integer by using a static_cast to int:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <iostream>
int main()
{
enum class Color
{
red,
blue
};
Color color{ Color::blue };
std::cout << color; // won't work, because there's no implicit conversion to int
std::cout << static_cast<int>(color); // will print 1
return 0;
}
|
There’s little reason to use normal enumerated types instead of enum classes.