如何在C++中创建编译期常量全局类?嵌入式场景适配
嵌入式C++中实现编译期构造的常量类/结构体
在嵌入式场景下,要实现和C语言类似的、编译期构造且放在只读数据段的常量实例,C++可以通过constexpr关键字轻松实现,完全避开全局构造函数。下面分两种常用写法说明:
1. 兼容C风格的结构体写法
直接沿用你熟悉的C结构语法,只需要用constexpr替代C中的const(const也能工作,但constexpr明确强制编译期初始化):
#include <cstdint> // 先定义驱动方法结构体 struct drivermethods { void (*init)(void); void (*send)(uint8_t); }; // UART结构体定义 struct UART { uintptr_t baseaddr; int irqnum; const drivermethods* pMethods; }; // 编译期构造驱动方法实例 constexpr drivermethods driverX = { .init = []() { /* 这里写编译期可执行的初始化逻辑 */ }, .send = [](uint8_t data) { /* 发送逻辑 */ } }; // 编译期构造UART实例,会被放在只读数据段 constexpr UART uart0 = { .baseaddr = 0x40001000, .irqnum = 5, .pMethods = &driverX };
这段代码和你C语言的写法几乎一致,但constexpr确保所有初始化都在编译阶段完成,不会生成任何运行时构造代码,实例会被放在只读段(代码段的一部分)。
2. 带封装的C++类写法
如果想要用C++的类封装特性,同样可以通过constexpr构造函数实现编译期初始化:
#include <cstdint> struct drivermethods { void (*init)(void); void (*send)(uint8_t); }; class UART { public: // constexpr构造函数,强制编译期初始化 constexpr UART(uintptr_t addr, int irq, const drivermethods* methods) : baseaddr(addr), irqnum(irq), pMethods(methods) {} // 可以添加constexpr成员函数,编译期就能调用 constexpr uintptr_t getBaseAddr() const { return baseaddr; } private: uintptr_t baseaddr; int irqnum; const drivermethods* pMethods; }; constexpr drivermethods driverX = { .init = []() {}, .send = [](uint8_t) {} }; // 编译期构造UART类实例 constexpr UART uart0(0x40001000, 5, &driverX);
这种写法保留了C++的封装性,同时完全满足编译期构造、无全局构造函数的要求。
关键要点
constexpr是核心:它强制编译器在编译阶段完成初始化,确保实例被放在只读段,不会触发运行时构造。- 成员类型限制:要编译期构造的对象,成员必须是字面量类型(比如基本类型、指针、简单结构体),不能用需要运行时初始化的类型(比如
std::string)。 - 编译器版本:确保启用C11或更高版本(
constexpr在C11引入,C++14/17支持更多场景),嵌入式编译器一般都支持。 - 只读段验证:可以通过查看编译后的.map文件,确认
uart0等实例被放在.rodata段(只读数据段)。
内容的提问来源于stack exchange,提问作者user3696153
相关产品推荐
相关产品推荐

