乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

枚举类型

定义与作用

枚举用于定义一组具名的整型常量,提升代码可读性和类型安全性。C++ 提供两种枚举:

类型引入版本特点
enum(传统枚举)C++98值隐式转换为 int,标识符泄漏到外层作用域
enum class(限定作用域枚举)C++11强类型、作用域限定、不隐式转换为 int

使用场景:状态机、错误码、配置选项、星期/月份等离散值集合。

核心原理

传统 enum vs enum class 对比

底层类型指定

完整示例

示例一:HTTP 状态码处理——传统枚举的困境

场景说明:用传统 enum 和 enum class 分别实现 HTTP 状态码分类,展示后者的类型安全优势。

#include <iostream>
#include <string>
#include <cstdint>

// 传统枚举:值泄漏到外层作用域
enum HttpStatusOld {
    OK_OLD = 200,
    NOT_FOUND_OLD = 404,
    SERVER_ERROR_OLD = 500
};

// 其他枚举可能冲突
enum FileStatus {
    OK_FILE = 0,    // 与 HttpStatusOld::OK_OLD 命名不同,勉强能区分
    NOT_FOUND_FILE = -1
};

// enum class:限定作用域 + 强类型
enum class HttpStatus : uint16_t {
    OK = 200,
    Created = 201,
    MovedPermanently = 301,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404,
    InternalServerError = 500
};

// 另一个 enum class,名称可以相同
enum class DatabaseError : uint8_t {
    OK = 0,           // 与 HttpStatus::OK 不冲突
    NotFound = 1,     // 与 HttpStatus::NotFound 不冲突
    ConnectionFailed = 2,
    Timeout = 3
};

std::string status_text(HttpStatus status) {
    switch (status) {
        case HttpStatus::OK:                  return "请求成功";
        case HttpStatus::Created:             return "已创建";
        case HttpStatus::MovedPermanently:    return "永久重定向";
        case HttpStatus::BadRequest:          return "请求错误";
        case HttpStatus::Unauthorized:        return "未授权";
        case HttpStatus::NotFound:            return "未找到";
        case HttpStatus::InternalServerError: return "服务器内部错误";
        default:                              return "未知状态";
    }
}

bool is_client_error(HttpStatus status) {
    auto code = static_cast<uint16_t>(status);
    return code >= 400 && code < 500;
}

int main() {
    // ---- 传统枚举的问题 ----
    int old_status = OK_OLD;         // 隐式转换为 int,丢失类型信息
    old_status = 999;                // 可以赋任意值,没有类型检查
    std::cout << "传统枚举: " << old_status << std::endl;

    // ---- enum class 的使用 ----
    HttpStatus status = HttpStatus::OK;

    // int x = status;               // 编译错误:不能隐式转换
    int code = static_cast<int>(status); // 必须显式转换

    std::cout << "\n===== HTTP 响应 =====" << std::endl;
    std::cout << "状态: " << status_text(status)
              << " (" << code << ")" << std::endl;

    // 遍历多个状态
    std::vector<HttpStatus> responses = {
        HttpStatus::OK,
        HttpStatus::NotFound,
        HttpStatus::InternalServerError,
        HttpStatus::Unauthorized
    };

    for (auto s : responses) {
        std::cout << "  " << static_cast<int>(s) << ": "
                  << status_text(s);
        if (is_client_error(s)) {
            std::cout << " [客户端错误]";
        }
        std::cout << std::endl;
    }

    // ---- 不同枚举类型不可混用 ----
    DatabaseError db_err = DatabaseError::NotFound;
    // if (db_err == HttpStatus::NotFound) {} // 编译错误:不同类型

    std::cout << "\n数据库错误码: " << static_cast<int>(db_err) << std::endl;

    return 0;
}

预期输出:

传统枚举: 999

===== HTTP 响应 =====
状态: 请求成功 (200)
  200: 请求成功
  404: 未找到 [客户端错误]
  500: 服务器内部错误
  401: 未授权 [客户端错误]

数据库错误码: 1

逐段分析:

  • 传统 enum HttpStatusOld 的值可以直接赋给 int,也能把任意整数赋给枚举变量——类型不安全
  • OK_OLD 和 OK_FILE 需要不同命名来避免冲突,因为传统枚举的值会泄漏到外层作用域
  • enum class HttpStatus 的值需要通过 HttpStatus::OK 访问,不同枚举可以有同名值
  • 不同类型枚举之间不能比较(db_err == HttpStatus::NotFound 编译失败)
  • 显式转换 static_cast<int>(status) 明确表达了"我知道自己在做什么"的意图

示例二:状态机 + 底层类型指定

场景说明:TCP 连接状态机,使用 enum class 搭配 uint8_t 底层类型减少内存占用。

#include <iostream>
#include <string>
#include <cstdint>

// 指定底层类型为 uint8_t:每个状态只占 1 字节
enum class TcpState : uint8_t {
    Closed = 0,
    Listen,
    SynSent,
    SynReceived,
    Established,
    FinWait1,
    FinWait2,
    CloseWait,
    Closing,
    LastAck,
    TimeWait
};

std::string state_name(TcpState s) {
    switch (s) {
        case TcpState::Closed:       return "CLOSED";
        case TcpState::Listen:       return "LISTEN";
        case TcpState::SynSent:      return "SYN_SENT";
        case TcpState::SynReceived:  return "SYN_RECEIVED";
        case TcpState::Established:  return "ESTABLISHED";
        case TcpState::FinWait1:     return "FIN_WAIT_1";
        case TcpState::FinWait2:     return "FIN_WAIT_2";
        case TcpState::CloseWait:    return "CLOSE_WAIT";
        case TcpState::Closing:      return "CLOSING";
        case TcpState::LastAck:      return "LAST_ACK";
        case TcpState::TimeWait:     return "TIME_WAIT";
        default:                     return "UNKNOWN";
    }
}

class TcpConnection {
public:
    TcpConnection() : state_(TcpState::Closed) {}

    void open() {
        if (state_ == TcpState::Closed) {
            transition(TcpState::SynSent);
        }
    }

    void receive_syn_ack() {
        if (state_ == TcpState::SynSent) {
            transition(TcpState::Established);
        }
    }

    void close() {
        if (state_ == TcpState::Established) {
            transition(TcpState::FinWait1);
        }
    }

    TcpState state() const { return state_; }

private:
    TcpState state_;

    void transition(TcpState new_state) {
        std::cout << "  " << state_name(state_)
                  << " → " << state_name(new_state) << std::endl;
        state_ = new_state;
    }
};

int main() {
    TcpConnection conn;

    std::cout << "=== TCP 连接状态机 ===" << std::endl;
    std::cout << "初始状态: " << state_name(conn.state()) << std::endl;

    conn.open();
    conn.receive_syn_ack();

    std::cout << "当前状态: " << state_name(conn.state()) << std::endl;

    conn.close();

    // 确认内存占用
    std::cout << "\nsizeof(TcpState) = " << sizeof(TcpState)
              << " 字节 (uint8_t 底层类型)" << std::endl;
    std::cout << "sizeof(TcpConnection) = " << sizeof(TcpConnection)
              << " 字节" << std::endl;

    return 0;
}

预期输出:

=== TCP 连接状态机 ===
初始状态: CLOSED
  CLOSED → SYN_SENT
  SYN_SENT → ESTABLISHED
当前状态: ESTABLISHED
  ESTABLISHED → FIN_WAIT_1

sizeof(TcpState) = 1 字节 (uint8_t 底层类型)
sizeof(TcpConnection) = 1 字节

逐段分析:

  • enum class TcpState : uint8_t 指定底层类型为 uint8_t,保证枚举变量只占 1 字节
  • 状态转换在 transition 函数中统一处理并输出日志
  • switch 中每个 case 都写完整 TcpState:: 前缀——这是 enum class 的代价(代码更长),但换来类型安全
  • 在嵌入式或网络协议实现中,enum class + 底层类型指定能精确控制内存布局

易错场景与面试考点

易错场景

场景错误表现正确做法
传统 enum 值隐式转为 int类型信息丢失,可赋值任意整数使用 enum class
不同枚举类型的值混用逻辑错误,编译器不报错enum class 禁止跨类型比较
enum class 的 switch 漏写作用域前缀case OK: 编译失败必须写 case Color::OK:
忘记指定底层类型导致内存浪费结构体大小不可控明确指定 : uint8_t 等
传统 enum 前向声明C++98 不允许,C++11 需要指定底层类型enum class 默认支持前向声明

常见面试问题

  1. enum 和 enum class 的核心区别?何时用哪个?——前者值隐式转 int、作用域泄漏、类型不安全;后者强类型、作用域限定、不隐式转换。新代码统一用 enum class。

  2. 为什么 enum class 的值不能隐式转换为 int?——这是设计目标:防止意外将枚举值当作整数使用(如算术运算、不同枚举混用),强制程序员显式写出转换意图。

  3. C++11 中 enum class 的底层类型如何指定?有什么好处?——enum class E : uint8_t {}; 指定底层类型。好处:控制内存占用(尤其嵌入式)、支持前向声明、明确取值范围。

  4. 如何遍历 enum class 的所有值?——C++ 没有内置反射机制。通常需要手动定义范围常量或使用第三方库(如 magic_enum)。或者定义 First 和 Last 哨兵值配合循环。

  5. 传统 enum 为什么容易造成命名污染?——枚举值被注入到枚举所在的作用域,不同枚举不能有同名值。enum class 的值必须通过类型名限定访问,解决了这一问题。

小结

  • 新代码统一使用 enum class,传统 enum 仅用于兼容旧代码
  • enum class 提供强类型检查、作用域限定、不隐式转换为 int
  • 建议指定底层类型(如 : uint8_t),控制内存占用
  • 需要整数运算时显式 static_cast
  • 不同 enum class 可以有同名值,互不干扰
上一页
类型转换
下一页
引用与指针