为什么enum class是你在C++中的新好朋友?
每个开始学习C++或中断后重新回归的人,迟早都会面临一个两难选择:使用老牌的enum,还是使用更现代的enum class?在Stack Overflow上,问题“为什么enum class被认为比普通enum更安全?”获得了数千次浏览——这并非偶然。转向作用域枚举(enum class的官方名称)不仅仅是追赶潮流,而是迈向更可靠、更易读代码的实际一步。
让我们深入探讨旧enum的危险之处,以及enum class如何帮助避免一整类错误。如果你在用C++编程,并且仍然使用无作用域的enum,那么这篇文章就是为你准备的。
旧enum的三大错误(以及enum class如何修复它们)
1️⃣ 污染全局作用域
旧enum最常见的问题是其常量“泄漏”到周围命名空间中。假设你有两个枚举:
// 糟糕:旧enumenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // 编译错误!Blue已被定义Color中的常量Blue已被占用,你无法在Feeling中使用相同的名称。在包含数百个文件的大型项目中,这会导致真正的头痛。你不得不编造像Color_Blue或COLOR_BLUE这样的长名称,这会使代码变得丑陋。
使用enum class的解决方案:
// 好:enum class(作用域枚举)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // 没问题!没有冲突现在Blue存在于其自己的类内部。要访问它,你需要写Color::Blue或Feeling::Blue。这不仅解决了命名冲突问题,还使代码自文档化——一眼就能看出值属于哪个类型。
2️⃣ 隐式转换为int(以及反向转换)
旧enum会隐式转换为整数类型。乍一看这很方便,但在实践中,它是难以发现的bug的根源。
// 危险的旧enumenum WarningLevel { Low, Medium, High };
void setVolume(int level) { if (level > 10) level = 10; // ...}
setVolume(High); // 能运行!但High = 2,而不是音量级别// 你意外地传入了警报级别而不是音量——编译器保持沉默编译器会毫无警告地接受这样的代码。如果你在函数中弄乱了参数顺序——bug就会进入运行时。
enum class如何保护你:
enum class WarningLevel { Low, Medium, High };
void setVolume(int level);
setVolume(WarningLevel::High); // 编译错误!// 没有从enum class到int的隐式转换编译器会强制你显式转换值(如果确实需要)。这意味着你意识到每一次转换,而不是依赖“侥幸通过”。
// 如果确实需要——显式进行setVolume(static_cast<int>(WarningLevel::High)); // 明确且清晰3️⃣ 未定义的底层类型
旧enum不保证底层使用哪种整数类型。编译器自行选择(通常是int),这可能导致在不同编译器或平台之间传递时出现意外。
// 大小可能不同!enum Small { A, B, C }; // 可能是int,也可能是charenum Big { X = 100000, Y, Z }; // 仍然可能是int使用enum class,你可以显式指定底层类型,从而获得完全的控制权。