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

Arduino开发中派生类传入基类形参位置运行异常问题

问题原因

你碰到的是典型的C++对象切片+指针步长不匹配问题:

  • Cycle类同时继承Event和EventManager,单对象内存大小远大于纯Event对象
  • EventManager内部用Event*类型存储事件列表,C++对基类指针做偏移计算时,会固定按sizeof(Event)作为步长找下一个元素。当传入的是Cycle数组时,偏移量完全错配,访问第二个元素时实际指向的内存根本不是第二个Cycle的起始地址,自然读不到正确初始化的name属性,还会触发内存越界的未定义行为。
  • 传入纯Event数组时,元素大小和指针步长完全匹配,所以运行正常。
修复方法

核心是不要用基类数组直接存派生类对象,改成存储基类指针,从根源避免切片和步长错误。

1. 修改EventManager类

把内部事件存储从对象数组改成指针数组,对应调整访问逻辑:

// EventManager.h  替换原有类定义
class EventManager
{
public:
    EventManager(size_t n_events = 0, Event **events = nullptr);
    void loop();

private:
    size_t n_events;
    Event **events; // 二级指针,存储事件对象的指针
    size_t current_event;
    bool cycle_ended;
    bool check_event_end();
    void end_current_event();
};
// EventManager.cpp  替换原有实现
EventManager::EventManager(size_t n_events, Event **events)
    : n_events(n_events), events(events), current_event(0), cycle_ended(false) {}

bool EventManager::check_event_end()
{
    return events[current_event]->ended();
}

void EventManager::end_current_event()
{
    events[current_event]->end();
    current_event == n_events - 1 ? cycle_ended = true : current_event++;
}

void EventManager::loop()
{
    if (cycle_ended)
        return;

    events[current_event]->run();

    if (check_event_end())
        end_current_event();
}

2. 给Event类加虚函数支持多态

需要多态调用的方法加virtual关键字,确保派生类重写的逻辑能被正确触发:

// Event.h  类定义中给对应方法加virtual
class Event
{
public:
    Event(String name, Duration duration);
    virtual bool ended();
    virtual void start();
    virtual void run();
    virtual void end();

private:
    // 原有私有成员不变
};

3. 补全Cycle类的调度逻辑

现在的Cycle类继承了EventManager但没有触发内部子事件调度,需要重写run、end方法,同时显式指定作用域避免多继承的二义性:

// Cycle.h
class Cycle : public Event, public EventManager
{
public:
    Cycle(String name, Duration duration, size_t event_count, Event **events);
    void run() override;
    void end() override;
};
// Cycle.cpp
Cycle::Cycle(String name, Duration duration, size_t event_count, Event **events)
    : Event(name, duration), EventManager(event_count, events){}

void Cycle::run()
{
    Event::start();
    EventManager::loop(); // 执行内部子事件调度
}

void Cycle::end()
{
    Event::end();
    // 可在此处添加子事件重置逻辑,支持Cycle重复运行
}

4. 修改main.cpp的实例化逻辑

用指针数组组装事件列表,和EventManager的入参格式匹配:

#include <Arduino.h>
#include "EventManager.h"
#include "Event.h"
#include "Cycle.h"

// 子事件
Event event1("event1", Duration{0, 0, 5});
Event event2("event2", Duration{0, 0, 5});
Event* sub_events[] = {&event1, &event2};

// 顶层周期
Cycle cycle1("First cycle", Duration{0, 0, 10}, 2, sub_events);
Cycle cycle2("Second cycle", Duration{0, 0, 10}, 2, sub_events);
Event* cycle_list[] = {&cycle1, &cycle2};

EventManager event_manager(2, cycle_list);

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  event_manager.loop();
}
注意事项
  • 多继承场景下如果两个基类有同名方法,调用时必须显式写清类作用域,否则会报二义性编译错误
  • 只要用到基类指针指向派生类对象的场景,就不要直接传派生类对象数组,指针偏移计算不会自动适配派生类的内存大小,必须用指针数组存储。

内容的提问来源于stack exchange,提问作者seddouguim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:57:25