> 技术文档 > 深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!


本篇摘要

  • 在 C++11 中引入了 枚举类(enum class),它是对传统 enum 的现代化改进,解决了传统枚举的多个问题,如命名冲突、隐式类型转换、作用域污染等。

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

一·传统枚举

如:

enum Color { RED, GREEN, BLUE};enum Light { RED, // 编译错误!命名冲突 YELLOW};
  • 我们会发现枚举的成员有冲突,因此会导致下面编译的报错:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

传统 enum 存在的问题:

  • 命名冲突:不同枚举之间不能有相同的名字:

就是这个例子:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

  • 作用域污染:枚举值暴露在全局或当前命名空间:

比如这里我们可以直接通过外接访问到这个成员:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

  • 隐式转换:枚举值可以自动转换为 int

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

下面我们运行下:

root@hcss-ecs-7d13:/home/sw/linux_learn/extra_knowledge/enum_class# ./a.out0

发现结果就是0,明显自动隐式转换了。

  • 安全性差:容易误用、类型不安全。

这里就是我们上面暴露的问题的总结了,因此下面我们引入了C++的枚举类!

二·C++的枚举类

首先总结下它的特点,也就是对上面缺点的修正:

特性 说明 作用域隔离 枚举值只能通过 枚举类名::值 访问 类型安全 不允许隐式转换为 int 可指定底层类型 可控制枚举值的存储大小 可读性高 代码结构清晰,易于维护

具体用法剖析

一般形式(当然我们一般默认成员都显转int,因此底层类型一般不写):

enum class 枚举类名 [: 底层类型] { 枚举值1, 枚举值2, ...};

简单使用

比如还是那上面我们那个例子说明:

enum class Color { Red, Green, Blue};enum class Light :uint32_t{ Red, Yellow};
  • 此时再编译就不会报错了!

此时我们需要突破类域方式去访问了:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

运行结果:

root@hcss-ecs-7d13:/home/sw/linux_learn/extra_knowledge/enum_class# ./a.outColor is Red

枚举类的底层类型Underlying Type

enum class Color : uint8_t { Red, Green, Blue};
  • 每个成员8个比特,理论这个枚举类大小就是1字节,下面验证下:

sizeof(Color) 后结果:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

常见底层类型:

  • int(默认)
  • uint8_t / int8_t
  • uint16_t / int16_t
  • uint32_t / int32_t
  • uint64_t / int64_t

枚举类与整数的转换

由于 enum class 不允许隐式转换,必须显式转换。

只能:

static_cast<int>(c);

下面来验证下:

enum class Color : uint8_t { Red, Green, Blue};
Color c = Color::Green;int value = static_cast<int>(c);std::cout << value << std::endl; 

结果符合预期:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

但是要是反过来呢?

Color c2 = static_cast<Color>(2);
  • 不建议这样使用,如果整数不在枚举范围内,行为是未定义的。

最后一个特点就明显不用说了吧!

枚举类作为函数参数和返回值

作为函数参数

void setColor(Color c) { std::cout << \"Setting color to: \" << static_cast<int>(c) << std::endl;} 

输出:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

作为函数返回值

Color getFavoriteColor() { return Color::Blue;}

这里如果我们强转成对应的类型uint8_t 此时就是这样:

 std::cout<<static_cast< uint8_t>(favorite)<<std::endl;

结果:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

  • 我们发现它是空白其实,因为这杯cout把2转义成char类型,也就是整数2对应的ASIIC码: STX(Start of Text),它是不可见的,所以我们打印出来看不到!

因此需要:

 std::cout<<static_cast< int>(favorite)<<std::endl;

就如下:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

拓展使用

结合switch进行转义选择使用

也就是我们通过对枚举成员进行switch操作,对指定枚举量安排指定操作(其他操作功能可自行拓展):

简单版如下:

enum class Color : uint8_t { Red, Green, Blue};void printColor(Color c) { switch (c) { case Color::Red: std::cout << \"Color: Red\\n\"; break; case Color::Green: std::cout << \"Color: Green\\n\"; break; case Color::Blue: std::cout << \"Color: Blue\\n\"; break; default: std::cout << \"Unknown color\\n\"; }}

下面我们测试一下:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

获取枚举类的底层类型

  • 使用 中的 std::underlying_type 可以获取枚举类的底层类型。

下面我们测试下:

代码:

#include #include enum class LevelType : uint16_t{ UNKNOW = 0, DEBUG, INFO, WARN, ERROR};int main(){ using underlying_type = std::underlying_type<LevelType>::type; std::cout << \"Underlying type size: \" << sizeof(underlying_type) << \" bytes\\n\"; return 0;}

结果如下:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

  • 符合预期!

封装枚举类:添加描述和状态码

  • 虽然 enum class 提供了类型安全和作用域隔离的优势,但它本身不支持直接附加描述信息或状态码。因此,我们常常需要通过类封装的方式,为枚举类添加描述、状态码、转换函数等功能。

首先要知道和之前的普通枚举一样是可以类内自己给值的:

 enum class Code { OK=200, NotFound=404, InternalServerError=500 };

但是下面我们通过封装类及提供接口方式来完成对应的设置获取等:

基于上述测试代码:

#include #include class HttpStatus {public: enum class Code { OK, NotFound, InternalServerError }; explicit HttpStatus(Code code) : code_(code) {} // 获取状态码数值 int getStatusCode() const { switch (code_) { case Code::OK: return 200; case Code::NotFound: return 404; case Code::InternalServerError: return 500; } return -1; // 不应到达 } // 获取状态描述 std::string getDescription() const { switch (code_) { case Code::OK: return \"OK\"; case Code::NotFound: return \"Not Found\"; case Code::InternalServerError: return \"Internal Server Error\"; } return \"Unknown Status\"; } // 打印状态信息 void print() const { std::cout << getStatusCode() << \" \" << getDescription() << std::endl; }private: Code code_;};int main(){ HttpStatus ok(HttpStatus::Code::OK); HttpStatus notFound(HttpStatus::Code::NotFound); HttpStatus serverError(HttpStatus::Code::InternalServerError); ok.print();  notFound.print(); serverError.print(); return 0;}
  • 运行后可以发现:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

  • 这里就是我们通过对应的枚举成员码通过Switch进行对应的数字和描述的选择(也是常用作对应的比如http等)

通过类封装,我们可以为枚举类添加描述、状态码、转换方法等高级功能,同时在 switch 中显式处理所有枚举值,确保逻辑完整性和代码健壮性。

总结

一句话:

C++ 枚举类(enum class)是现代 C++ 编程中推荐使用的枚举形式,它解决了传统枚举的诸多问题,提高了代码的安全性、可读性和可维护性。

下面博主准备了关于它使用的顺口溜,帮助大家记忆:

枚举类,C++11,命名不冲突,作用域严。enum class 加限定,Red要加Color::才安全。隐式转换不允许,int转它要static_cast显。底层类型可指定,uint8_t省空间,嵌入式欢。switch要全覆盖,case别漏防出错,default保平安。封装功能更强大,加描述、状态码,类来管。类型安全最重要,enum class替旧版,代码稳!

传统枚举与枚举类对比:

特性 传统 enum 枚举类 enum class 命名冲突 容易发生 不会发生(作用域隔离) 隐式转换 允许 不允许 作用域 枚举值暴露在外部 枚举值只在类内部可见 可读性 较差 更清晰、更安全 底层类型 不可指定 支持指定(如 int, char

可视化流程图:

深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

本篇分享枚举类使用知识到这里,欢迎大家继续订阅本专栏学习更多知识来充实大脑。

米锋网