首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入现代 C++:enum class 全面解析,彻底告别枚举踩坑!

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

作者头像
羑悻的小杀马特.
发布2025-08-05 08:23:24
发布2025-08-05 08:23:24
5890
举报
文章被收录于专栏:杀马特杀马特

本篇摘要

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

一·传统枚举

如:

代码语言:javascript
复制
enum Color {
    RED,
    GREEN,
    BLUE
};

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

传统 enum 存在的问题:

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

就是这个例子:

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

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

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

下面我们运行下:

代码语言:javascript
复制
root@hcss-ecs-7d13:/home/sw/linux_learn/extra_knowledge/enum_class# ./a.out
0

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

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

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

二·C++的枚举类

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

特性

说明

作用域隔离

枚举值只能通过 枚举类名::值 访问

类型安全

不允许隐式转换为 int

可指定底层类型

可控制枚举值的存储大小

可读性高

代码结构清晰,易于维护

具体用法剖析

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

代码语言:javascript
复制
enum class 枚举类名 [: 底层类型] {
    枚举值1,
    枚举值2,
    ...
};
简单使用

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

代码语言:javascript
复制
enum class Color {
    Red,
    Green,
    Blue
};

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

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

运行结果:

代码语言:javascript
复制
root@hcss-ecs-7d13:/home/sw/linux_learn/extra_knowledge/enum_class# ./a.out
Color is Red
枚举类的底层类型Underlying Type
代码语言:javascript
复制
enum class Color : uint8_t {
    Red,
    Green,
    Blue
};
  • 每个成员8个比特,理论这个枚举类大小就是1字节,下面验证下:

sizeof(Color) 后结果:

常见底层类型:

  • int(默认)
  • uint8_t / int8_t
  • uint16_t / int16_t
  • uint32_t / int32_t
  • uint64_t / int64_t
枚举类与整数的转换

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

只能:

代码语言:javascript
复制
static_cast<int>(c);

下面来验证下:

代码语言:javascript
复制
enum class Color : uint8_t {
    Red,
    Green,
    Blue
};
代码语言:javascript
复制
Color c = Color::Green;
int value = static_cast<int>(c);
std::cout << value << std::endl; 

结果符合预期:

但是要是反过来呢?

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

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

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

作为函数参数
代码语言:javascript
复制
void setColor(Color c) {
    std::cout << "Setting color to: " << static_cast<int>(c) << std::endl;
} 

输出:

作为函数返回值
代码语言:javascript
复制
Color getFavoriteColor() {
    return Color::Blue;
}

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

代码语言:javascript
复制
 std::cout<<static_cast< uint8_t>(favorite)<<std::endl;

结果:

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

因此需要:

代码语言:javascript
复制
 std::cout<<static_cast< int>(favorite)<<std::endl;

就如下:

拓展使用

结合switch进行转义选择使用

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

简单版如下:

代码语言:javascript
复制
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";
    }
}

下面我们测试一下:

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

下面我们测试下:

代码:

代码语言:javascript
复制
#include <iostream>
#include <type_traits>

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;
}

结果如下:

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

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

代码语言:javascript
复制
 enum class Code {
        OK=200,
        NotFound=404,
        InternalServerError=500
    };

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

基于上述测试代码:

代码语言:javascript
复制
#include <iostream>
#include <string>

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;
}
  • 运行后可以发现:
  • 这里就是我们通过对应的枚举成员码通过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)

可视化流程图:

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本篇摘要
  • 一·传统枚举
    • 传统 enum 存在的问题:
  • 二·C++的枚举类
    • 具体用法剖析
      • 简单使用
      • 枚举类的底层类型(Underlying Type)
      • 枚举类与整数的转换
    • 枚举类作为函数参数和返回值
      • 作为函数参数
      • 作为函数返回值
    • 拓展使用
      • 结合switch进行转义选择使用
      • 获取枚举类的底层类型
      • 封装枚举类:添加描述和状态码
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档