C++ 枚举类型详解

C++中有两种主要的枚举类型:传统枚举(enum)有作用域枚举(enum class)

1. 传统枚举(Unscoped Enum)

基本语法

enum EnumName {
    VALUE1,
    VALUE2,
    VALUE3
};

示例代码

#include <iostream>
using namespace std;

// 传统枚举
enum Color {
    RED,    // 0
    GREEN,  // 1
    BLUE    // 2
};

enum Weekday {
    MONDAY = 1,
    TUESDAY,    // 2
    WEDNESDAY,  // 3
    THURSDAY,   // 4
    FRIDAY = 10,
    SATURDAY,   // 11
    SUNDAY      // 12
};

int main() {
    Color c1 = RED;
    Color c2 = GREEN;
    
    cout << "c1: " << c1 << endl;  // 输出: 0
    cout << "c2: " << c2 << endl;  // 输出: 1
    
    Weekday today = FRIDAY;
    cout << "Today is: " << today << endl;  // 输出: 10
    
    // 可以隐式转换为整型
    int colorValue = BLUE;  // 2
    cout << "Blue value: " << colorValue << endl;
    
    return 0;
}

传统枚举的问题

enum Color { RED, GREEN, BLUE };
enum TrafficLight { RED, YELLOW, GREEN };  // 错误!命名冲突

int main() {
    Color c = RED;
    int x = c;  // 隐式转换,可能不是期望的行为
    return 0;
}

2. 有作用域枚举(Scoped Enum - enum class)

基本语法

enum class EnumName {
    VALUE1,
    VALUE2,
    VALUE3
};

示例代码

#include <iostream>
using namespace std;

// 有作用域枚举
enum class Color {
    RED,
    GREEN,
    BLUE
};

enum class TrafficLight {
    RED,
    YELLOW,
    GREEN
};

enum class FileStatus : uint8_t {
    OK = 0,
    NOT_FOUND = 1,
    PERMISSION_DENIED = 2
};

int main() {
    Color c1 = Color::RED;
    TrafficLight light = TrafficLight::RED;  // 没有命名冲突!
    
    // 不能隐式转换为整型
    // int x = c1;  // 错误!
    int x = static_cast<int>(c1);  // 需要显式转换
    cout << "Color value: " << x << endl;  // 输出: 0
    
    // 比较操作
    if (c1 == Color::RED) {
        cout << "It's red!" << endl;
    }
    
    // 指定底层类型的枚举
    FileStatus status = FileStatus::NOT_FOUND;
    auto statusValue = static_cast<uint8_t>(status);
    cout << "Status value: " << static_cast<int>(statusValue) << endl;
    
    return 0;
}

3. 枚举的高级特性

指定底层类型

#include <iostream>
#include <cstdint>
using namespace std;

// 指定底层类型
enum class SmallEnum : char {
    A = 'a',
    B = 'b',
    C = 'c'
};

enum class IntEnum : int {
    VALUE1 = 100,
    VALUE2 = 200,
    VALUE3 = 300
};

int main() {
    SmallEnum se = SmallEnum::A;
    cout << "Size of SmallEnum: " << sizeof(se) << " bytes" << endl;
    cout << "Value: " << static_cast<char>(se) << endl;
    
    IntEnum ie = IntEnum::VALUE2;
    cout << "Size of IntEnum: " << sizeof(ie) << " bytes" << endl;
    cout << "Value: " << static_cast<int>(ie) << endl;
    
    return 0;
}

枚举与switch语句

#include <iostream>
using namespace std;

enum class Operation {
    ADD,
    SUBTRACT,
    MULTIPLY,
    DIVIDE
};

double calculate(double a, double b, Operation op) {
    switch (op) {
        case Operation::ADD:
            return a + b;
        case Operation::SUBTRACT:
            return a - b;
        case Operation::MULTIPLY:
            return a * b;
        case Operation::DIVIDE:
            if (b != 0) return a / b;
            else throw runtime_error("Division by zero");
        default:
            throw runtime_error("Unknown operation");
    }
}

int main() {
    double result = calculate(10.5, 2.5, Operation::MULTIPLY);
    cout << "Result: " << result << endl;  // 输出: 26.25
    return 0;
}

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

#include <iostream>
#include <string>
using namespace std;

enum class LogLevel {
    DEBUG,
    INFO,
    WARNING,
    ERROR
};

string logLevelToString(LogLevel level) {
    switch (level) {
        case LogLevel::DEBUG: return "DEBUG";
        case LogLevel::INFO: return "INFO";
        case LogLevel::WARNING: return "WARNING";
        case LogLevel::ERROR: return "ERROR";
        default: return "UNKNOWN";
    }
}

class Logger {
public:
    void setLevel(LogLevel level) {
        currentLevel = level;
    }
    
    void log(LogLevel level, const string& message) {
        if (level >= currentLevel) {
            cout << "[" << logLevelToString(level) << "] " << message << endl;
        }
    }

private:
    LogLevel currentLevel = LogLevel::INFO;
};

int main() {
    Logger logger;
    logger.setLevel(LogLevel::DEBUG);
    logger.log(LogLevel::INFO, "Application started");
    logger.log(LogLevel::DEBUG, "Debug information");
    
    return 0;
}

4. 枚举的最佳实践

使用有作用域枚举(enum class)

// 推荐:使用enum class避免命名污染
enum class Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
};

// 不推荐:传统enum容易造成命名冲突
enum OldDirection {
    NORTH,  // 可能与其他枚举冲突
    SOUTH,
    EAST, 
    WEST
};

为枚举添加操作方法

#include <iostream>
#include <string>
using namespace std;

enum class Status {
    PENDING,
    PROCESSING,
    COMPLETED,
    FAILED
};

// 为枚举添加实用方法
namespace StatusUtils {
    string toString(Status status) {
        switch (status) {
            case Status::PENDING: return "Pending";
            case Status::PROCESSING: return "Processing";
            case Status::COMPLETED: return "Completed";
            case Status::FAILED: return "Failed";
            default: return "Unknown";
        }
    }
    
    bool isFinal(Status status) {
        return status == Status::COMPLETED || status == Status::FAILED;
    }
}

int main() {
    Status taskStatus = Status::PROCESSING;
    
    cout << "Status: " << StatusUtils::toString(taskStatus) << endl;
    cout << "Is final: " << StatusUtils::isFinal(taskStatus) << endl;
    
    return 0;
}

总结

特性 传统枚举 (enum) 有作用域枚举 (enum class)
作用域 无作用域,会污染外层 有作用域,需要Enum::Value
隐式转换 允许隐式转换为int 不允许,需要显式转换
类型安全 较低 较高
底层类型 默认int,可指定 默认int,可指定
推荐使用 旧代码兼容 新项目推荐

最佳实践建议

  1. 在新代码中优先使用 enum class
  2. 为枚举指定合适的底层类型以节省内存
  3. 为枚举提供相关的工具函数
  4. 避免使用魔术数字,使用有意义的枚举值
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐