如何在C++中用继承优化Arduino逻辑块内存并统一管理派生类数组?
问题背景与需求
我有一个Java程序,可通过与门、或门这类逻辑功能块生成Arduino板的功能源码,让用户仅用逻辑块就能对Arduino编程。目前核心功能已实现,但遇到内存问题:
原使用如下struct定义功能块:
typedef struct blox { uint8_t IN1 : 1 ; // generic for most blocks uint8_t IN2 : 1 ; // generic for most blocks uint8_t IN3 : 1 ; // generic for most blocks uint8_t Q : 1 ; // generic for most blocks uint8_t pin : 5 ; // only inputs and output types need this uint8_t type : 4 ; // 16 combinations uint32_t oldTime ; // bad idea to use this amount of memory per block if only delays need it? const uint32_t interval ; // perhaps couple a function pointers or obj pointer to it? } FunctionBlock ; FunctionBlock block [ nBlocks ] ;
在主loop()中通过switch更新逻辑和硬编码链接,但该结构体包含所有块的冗余成员(如与门不需要pin、定时变量),若添加模拟输入、舵机、map()块等组件会导致内存浪费。
我尝试用C++继承设计类:
class FunctionBlock // AND, OR, MEMORY or NOT gates { public: uint8_t IN1 : 1 ; uint8_t IN2 : 1 ; uint8_t IN3 : 1 ; uint8_t Q : 1 ; uint8_t type ; // if I create derived classes for OR, AND, MEMORY and not gates, I may discard this variable } ; class IO : public FunctionBlock // INPUT, OUTPUT { uint8_t pin ; } ; class DELAY : public FunctionBlock { uint32_t prevTime ; const int delayTime ; } ; class MAP : public FunctionBlock { int32_t var ; // result = map( var, in1, in2, out1, out2 ) ; int32_t result; int32_t in1 ; int32_t in2 ; int32_t out1 ; int32_t out2 ; } ; // class analogIn, class constant, class comparator, class decadeCounter etc etc
类似Java中用ArrayList<FunctionBlock>管理派生类对象的方式,请问如何在C++中实现这类派生类的统一数组管理?
解决方案
在C++中统一管理派生类对象,核心是利用多态和基类指针/引用,结合Arduino嵌入式环境的内存特性,具体实现步骤如下:
1. 改造基类为抽象接口
首先将FunctionBlock改为抽象基类,定义所有功能块通用的更新接口,强制派生类实现自身逻辑,同时去掉冗余的type标记:
class FunctionBlock { public: uint8_t IN1 : 1; uint8_t IN2 : 1; uint8_t IN3 : 1; uint8_t Q : 1; // 纯虚函数:所有功能块必须实现自己的更新逻辑 virtual void update() = 0; // 虚析构函数:确保派生类对象能被正确销毁 virtual ~FunctionBlock() = default; };
2. 完善派生类实现
每个派生类补充自身专属成员变量,并实现update()方法:
// 与门逻辑块 class ANDGate : public FunctionBlock { public: void update() override { Q = IN1 & IN2 & IN3; } }; // IO块(区分输入/输出) class IO : public FunctionBlock { public: uint8_t pin; bool isInput; void update() override { if (isInput) { IN1 = digitalRead(pin); // 输入引脚值存入IN1 } else { digitalWrite(pin, Q); // 将Q值输出到引脚 } } }; // 延迟块 class DELAY : public FunctionBlock { public: uint32_t prevTime; const uint32_t delayTime; DELAY(uint32_t dt) : delayTime(dt), prevTime(millis()) {} void update() override { if (millis() - prevTime >= delayTime) { Q = IN1; // 延迟后传递输入值 prevTime = millis(); } } }; // MAP转换块 class MAP : public FunctionBlock { public: int32_t var; int32_t result; int32_t inMin; int32_t inMax; int32_t outMin; int32_t outMax; void update() override { result = map(var, inMin, inMax, outMin, outMax); Q = (result > 0) ? 1 : 0; // 转换为逻辑值存入Q } };
3. 统一管理派生类对象
针对Arduino内存有限的特性,推荐两种管理方式:
方式1:静态基类指针数组(优先选择)
如果能预估最大功能块数量,用静态数组最节省内存,避免动态内存碎片:
const int MAX_BLOCKS = 10; // 根据需求调整最大值 FunctionBlock* blocks[MAX_BLOCKS] = {nullptr}; int blockCount = 0; // 添加功能块到数组 void addBlock(FunctionBlock* block) { if (blockCount < MAX_BLOCKS) { blocks[blockCount++] = block; } } void setup() { // 可以直接使用全局/静态实例,避免动态分配 static ANDGate andGate1; static IO inputPin2{2, true}; static DELAY delay1s(1000); addBlock(&andGate1); addBlock(&inputPin2); addBlock(&delay1s); } void loop() { // 遍历所有块执行更新 for (int i = 0; i < blockCount; i++) { if (blocks[i] != nullptr) { blocks[i]->update(); } } }
方式2:动态容器(按需使用)
如果需要动态增减块,且Arduino支持C++标准库的vector,可以用vector<FunctionBlock*>管理,但需注意内存碎片问题:
#include <vector> std::vector<FunctionBlock*> blocks; void setup() { blocks.push_back(new ANDGate()); blocks.push_back(new IO{3, false}); // 输出引脚3 } void loop() { for (auto* block : blocks) { block->update(); } } // 如需销毁块,记得调用delete释放内存 void cleanup() { for (auto* block : blocks) { delete block; } blocks.clear(); }
4. 内存优化注意事项
- 优先使用全局/静态派生类实例,减少
new/delete的动态内存操作,避免内存碎片。 - 如果必须动态分配,在程序结束或块不再使用时,务必调用
delete释放内存,防止内存泄漏。
内容的提问来源于stack exchange,提问作者bask185
相关产品推荐
相关产品推荐

