【CCG】枚举
作者:wallace-lai
发布:2024-02-25
更新:2024-02-25
枚举通常用于定义整数值集以及定义此类值集的类型,C++中有两种枚举,分别是普通的enum和enum class。
规则1:优选枚举而不是宏定义
宏定义和枚举相比,缺少作用域和类型方面的限制。宏定义在预处理阶段就会被处理掉,不会出现在诸如调试器这样的工具当中。
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
#define RED 0
#define PURPLE 1
#define BLUE 2
int webby = BLUE; // webby == 2; probably not what was desired
以上代码显示了使用宏定义的一大缺陷:在不同头文件中定义相同名称的宏可能导致你引用出错。相反,如果你使用enum class,这样的问题就不会出现。
enum class WebColor {
red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF
};
enum class ProductInfo {
red = 0,
purple = 1,
blue = 2
};
int webby = blue; // error : be specific
WebColor webby = WebColor::blue;
规则2:使用枚举来表示相关的命名常量
使用枚举来表示相应的命名常量后,在使用switch语句的过程中,编译器可以自动检查出关于case分支的一些异常情况。
enum class ProductInfo {
red = 0,
purple = 1,
blue = 2
};
void print(ProductInfo info)
{
switch (info) {
// 使用enum class使得编译器可以检查一些不寻常的case分支
switch (info) {
case ProductInfo::red :
cout << "red" << endl;
break;
case ProductInfo::purple :
cout << "purple" << endl;
break;
}
}
return;
}
比如对于上面的代码,编译器将给出如下的警告。这样就可以在编译过程中发现一些潜在的错误。
warning: enumeration value ‘blue’ not handled in switch [-Wswitch]
规则3:优选enum class而不是enum
具体原因在enum和enum class之间的不同这小节中已经说明了。概括来说是enum class在类型转换和作用域方面比enum有更加严格的限制,更加安全。
规则4:为了更加安全和方便,请在枚举之上定义操作函数
如下所示,请将操作定义在枚举之上(前提是它适合定义成枚举):
enum class Day {
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
Sun
};
Day &operator++(Day &d)
{
return d = (d == Day::Sun) ? Day::Mon :
static_cast<Day>(static_cast<int>(d) + 1);
}
注意上面的代码,尽管多次使用static_cast让代码变得不够美观,但是如果不使用则会得到一个错误。
Day &operator++(Day &d)
{
return d = (d == Day::Sun) ? Day::Mon :
Day{++d}; // error
}
规则5:禁止在枚举中使用全大写定义
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
enum class Product_info { RED, PURPLE, BLUE }; // syntax error
这条规则的目的主要是为了避免和宏定义产生命名冲突,因为宏定义是使用全大写的形式,所以禁止枚举也使用全大写的形式。
规则6:避免使用匿名枚举
如果你不对枚举进行命名,那么枚举值就无法和名字关联起来。尽量避免下面这种匿名枚举类型的出现。
// bad example
enum {
red = 0xFF0000,
scale = 4,
is_signed = 1
};
如果无法避免,那么请考虑使用constexpr进行替代:
constexpr int red = 0xFF0000;
constexpr short scale = 4;
constexpr bool is_signed = true;
规则7:只在需要时指定枚举值类型
C++中enum枚举值的默认类型是int,int是最方便用于读写的类型,这和C语言也是兼容的。只在有特殊需要的时候指定枚举值类型,除此之外一律使用默认的int类型。
// 为了节省内存空间
enum class Direction : char {
n, s, e, w,
ne, nw, se, sw
};
// 多余的类型指定,默认就是int32_t
enum class WebColor : int32_t {
red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF,
};
注意:在以下这种前置声明的场景下需要指定enum的类型
enum Flags : char;
void f(Flags);
// ...
enum Flags : char {
// ...
}
规则8:只在需要时指定枚举的具体值
enum class Col1 {
red,
yellow,
blue
};
enum class Col2 {
red = 1,
yellow = 2,
blue = 2
};
和C语言一样,不指定的情况下,C++从0开始连续不重复地分配枚举值。因此,只在有特殊需要的时候才需要指定枚举的具体值。