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
相关产品推荐
相关产品推荐

