You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 14:15:42