嵌入式C++教程——enum class¶
想象一下:你把一堆状态、模式、标志写成 enum,使用时却被隐式转换成 int,结果函数接收错了值、比较错了东西,bug 就笑着出来喝茶。enum class 就是 C++ 给你的安全带:强类型、作用域化、能指定底层类型——特别适合对内存、类型安全都有高要求的嵌入式世界。
一句概念总结¶
enum class(C++11)是强类型、受限作用域的枚举:
- 名字不会污染外部作用域(需要
E::Val访问); - 不会隐式转换为整数类型(避免误用);
- 可以指定底层类型(
uint8_t、int16_t等),对嵌入式节省空间很有用。
为什么嵌入式程序员会爱它¶
- 类型安全:防止把不同枚举或
int混到一起,减少逻辑错误。 - 控制大小:可以显式声明底层类型,节省 RAM/ROM(比如用
uint8_t)。 - 作用域清晰:
Status::OK不会和Error::OK撞名。 - 更易维护:代码可读性和意图明确,后续审查更少争吵。
基本例子:老 enum vs enum class¶
// 传统 enum(容易隐式转换)
enum Color { Red, Green, Blue };
void setColor(int c);
setColor(Red); // 隐式转换成 int,有可能传错值
// 强类型枚举
enum class EColor : uint8_t { Red, Green, Blue };
void setColor(EColor c);
setColor(EColor::Red); // 必须显式使用 EColor,安全
注意:enum class 的默认底层类型是 int,但你可以写成 : uint8_t 来强制它占 1 字节(对小 MCU 很重要)。
常见问题与实战技巧¶
1) 如何输出(打印)枚举值?¶
enum class 不能直接当整数打印,需要 static_cast:
或者写个小 helper:
template<typename E>
constexpr auto to_underlying(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
2) 指定底层类型节省内存¶
在嵌入式中,避免默认 int(可能是 32-bit)很重要:
用 uint8_t 后,变量只占一个字节,struct 排列也更紧凑。
3) 与 C 接口互操作¶
有些底层/库接口要求传 int 或 uint32_t,这时需要显式转换:
extern "C" void hw_set_mode(uint8_t mode);
enum class Mode : uint8_t { Low = 0, High = 1 };
hw_set_mode(static_cast<uint8_t>(Mode::High));
4) 枚举作为位标志(bitmask)¶
enum class 不支持位运算符默认重载。为可读性与类型安全,可以自己写运算符:
#include <type_traits>
template<typename E>
constexpr auto to_ut(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
enum class Flags : uint8_t {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Exec = 1 << 2
};
inline Flags operator|(Flags a, Flags b) {
return static_cast<Flags>(to_ut(a) | to_ut(b));
}
inline Flags& operator|=(Flags& a, Flags b) {
a = a | b;
return a;
}
inline Flags operator&(Flags a, Flags b) {
return static_cast<Flags>(to_ut(a) & to_ut(b));
}
inline bool any(Flags f) { return to_ut(f) != 0; }
// 使用
Flags perms = Flags::Read | Flags::Write;
if (any(perms & Flags::Write)) { /* 有写权限 */ }
许多项目会把这些运算符放在头文件并配一套宏或模板自动生成,方便且类型安全。
5) switch 语句的提醒¶
switch 仍然可用,但若没有处理所有枚举值,编译器警告(如 -Wswitch)会很有用。enum class 值要用 E::V:
switch (state) {
case SensorState::Off: break;
case SensorState::Init: break;
case SensorState::Ready: break;
case SensorState::Error: break;
}
加上 default 会抹去某些警告;有时候想利用编译器帮你检查穷尽性,就不要写 default,这样缺少分支会被提示。
查看完整可编译示例
// enum_class_basics.cpp
// 示例:传统 enum vs enum class 基本对比
#include <cstdint>
#include <iostream>
// 传统 enum(容易隐式转换)
enum Color { Red, Green, Blue };
void setColor(int c) {
std::cout << "Set color (int): " << c << "\n";
}
// 强类型枚举
enum class EColor : uint8_t { Red, Green, Blue };
void setEColor(EColor c) {
std::cout << "Set EColor: " << static_cast<int>(c) << "\n";
}
int main() {
// 传统 enum - 隐式转换
setColor(Red); // 隐式转换成 int,有可能传错值
setColor(42); // 也可以接受任意 int,不安全
// 强类型枚举 - 必须显式使用 EColor
setEColor(EColor::Red); // 必须显式使用 EColor,安全
// setEColor(42); // 编译错误!不能隐式转换
// 验证大小
static_assert(sizeof(EColor) == 1, "EColor 应该是 1 字节");
std::cout << "sizeof(Color): " << sizeof(Color) << "\n";
std::cout << "sizeof(EColor): " << sizeof(EColor) << "\n";
return 0;
}
查看位标志完整示例
// enum_class_bitflags.cpp
// 示例:为 enum class 实现位运算符支持
#include <cstdint>
#include <type_traits>
#include <iostream>
// 辅助函数:将枚举转换为其底层类型
template<typename E>
constexpr auto to_ut(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
// 定义位标志枚举
enum class Flags : uint8_t {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Exec = 1 << 2
};
// 位运算符重载
inline Flags operator|(Flags a, Flags b) {
return static_cast<Flags>(to_ut(a) | to_ut(b));
}
inline Flags& operator|=(Flags& a, Flags b) {
a = a | b;
return a;
}
inline Flags operator&(Flags a, Flags b) {
return static_cast<Flags>(to_ut(a) & to_ut(b));
}
inline Flags operator~(Flags a) {
return static_cast<Flags>(~to_ut(a));
}
inline bool any(Flags f) { return to_ut(f) != 0; }
int main() {
// 组合权限
Flags perms = Flags::Read | Flags::Write;
std::cout << "Permissions value: " << to_ut(perms) << "\n";
// 检查权限
if (any(perms & Flags::Write)) {
std::cout << "Has write permission\n";
}
// 添加执行权限
perms |= Flags::Exec;
std::cout << "Updated permissions: " << to_ut(perms) << "\n";
// 移除写权限
perms = perms & ~Flags::Write;
std::cout << "After removing Write: " << to_ut(perms) << "\n";
return 0;
}
查看内存优化与C接口互操作示例
// enum_class_memory.cpp
// 示例:使用 enum class 节省内存
#include <cstdint>
#include <iostream>
// 指定底层类型节省内存
enum class SensorState : uint8_t {
Off = 0,
Init = 1,
Ready = 2,
Error = 3
};
// 与 C 接口互操作
extern "C" void hw_set_mode(uint8_t mode);
enum class Mode : uint8_t { Low = 0, High = 1 };
// 模拟硬件函数
void hw_set_mode(uint8_t mode) {
std::cout << "Hardware mode set to: " << static_cast<int>(mode) << "\n";
}
int main() {
// 验证大小
static_assert(sizeof(SensorState) == 1, "SensorState 应该是 1 字节");
SensorState state = SensorState::Ready;
std::cout << "State value: " << static_cast<int>(state) << "\n";
std::cout << "State size: " << sizeof(state) << " byte\n";
// switch 语句
switch (state) {
case SensorState::Off: std::cout << "State: Off\n"; break;
case SensorState::Init: std::cout << "State: Init\n"; break;
case SensorState::Ready: std::cout << "State: Ready\n"; break;
case SensorState::Error: std::cout << "State: Error\n"; break;
}
// 与 C 接口互操作
hw_set_mode(static_cast<uint8_t>(Mode::High));
return 0;
}