如何在 Arduino 程序中正确初始化 C++ 对象?
Posted
技术标签:
【中文标题】如何在 Arduino 程序中正确初始化 C++ 对象?【英文标题】:How can I properly initialize C++ objects within an Arduino program? 【发布时间】:2021-02-18 16:40:01 【问题描述】:在我简单的 Arduino 项目中,为了保持整洁,我决定创建一个“模式管理器”来处理一种模式和另一种模式之间的传递。基本概念是每次我想改变模式,那么它会实例化下一个模式并替换上一个。
请注意,我在 OOP 和 Java / Scala 方面有 12 年以上的经验,但在 C++ 方面没有任何经验,只是让 Arduino 在没有太多结构的情况下完成其工作所需的东西。
经过一些研究,我设法创建了以下“界面”结构:
文件:modes/ModeManager.h
。这应该保持对当前模式的引用,并将循环和实例化下一个模式委托给它(每个模式都会知道哪个是下一个)
#ifndef __MODE_MANAGER__
#define __MODE_MANAGER__
#include <stdint.h>
#include "modes/Mode.h"
class ModeManager
private:
Mode *mode;
public:
ModeManager();
~ModeManager() ;
virtual void loop(uint8_t voltage);
;
#endif
文件:modes/Mode.h
是运行程序真正循环功能的实际“模式”。它还处理实例化下一个模式
#ifndef __MODE__
#define __MODE__
#include <stdint.h>
class Mode
public:
Mode()
virtual ~Mode()
virtual void loop(uint8_t voltage) = 0;
virtual void getNext(Mode * target) = 0;
;
#endif
文件:modes/ModeManager.cpp
#include "ModeManager.h"
#include <stdint.h>
#include <Arduino.h>
#include "modes/Mode.h"
#include "modes/DummyMode.h"
#define VOLTAGE_NEXT_MODE 5
ModeManager::ModeManager()
DummyMode cmode = DummyMode();
mode = & cmode; // I'm not sure how this should actually be done
void ModeManager::loop(uint8_t voltage)
if (voltage == VOLTAGE_NEXT_MODE)
Serial.println("Switching to next mode");
mode->getNext(mode);
else
Serial.println("Calling mode loop");
mode->loop(voltage); // From here, nothing.
文件:modes/DummyMode.h
#ifndef __DUMMY_MODE__
#define __DUMMY_MODE__
#include "modes/Mode.h"
class DummyMode : public Mode
public:
DummyMode();
~DummyMode()
virtual void loop(uint8_t voltage);
virtual void getNext(Mode * target);
;
#endif
文件:modes/DummyMode.cpp
#include "modes/DummyMode.h"
#include <Arduino.h>
DummyMode::DummyMode()
Serial.println("Initialization of dummy mode");
void DummyMode::loop(uint8_t voltage)
Serial.print("Voltage: ");
Serial.println(voltage);
void DummyMode::getNext(Mode * target)
DummyMode nextMode = DummyMode();
target = &nextMode;
最后是我的main.cpp
#include <Arduino.h>
#include "modes/ModeManager.h"
#include "modules/memory.h"
#include "modules/monitor.h"
ModeManager * modeManager;
void setup()
pinMode(A0, INPUT);
Serial.begin(9600);
Serial.println("Init");
ModeManager mm = ModeManager();
modeManager = & mm;
void loop(void)
uint8_t voltage = map(analogRead(A0), 0, 1024, 0, 5);
modeManager->loop(voltage);
现在,从理论上讲,我认为没有理由不这样做。在实践中,我 99.9% 确定我在初始化和指针方面做错了什么。 当我尝试运行此代码时,我得到以下序列号:
Init
Initialization of dummy mode
Calling mode loop
这意味着它在循环的第一次迭代中冻结,就在调用mode->loop(voltage);
之前
谁能指出我正确的方向?再说一次,我真的没有 C++ 的经验,我关于如何制作这种结构的知识来自 C++ 编程的各种在线资源,包括这里的一些答案,所以请多多包涵
【问题讨论】:
DummyMode nextMode = DummyMode();
在堆栈上创建一个局部变量,该变量在函数结束时被销毁,并且您将返回一个指向此堆栈空间的指针。
你对&
的所有使用都是错误的target = &nextMode;
modeManager = & mm;
你持有指向在作用域结束后不再存在的对象的指针。
如果您将 C++ 视为 C 和 Java 的混合体,那么您将很难编写 C++。它不是那些东西,并且足够复杂,值得阅读book。我不打算尝试回答,因为这段代码中存在太多问题,它会变成代码审查,而且那是一个不同的站点。
【参考方案1】:
这里:
DummyMode cmode = DummyMode();
您正在创建一个具有自动生命周期的DummyMode
实例cmode
(通常称为堆栈上)。
然后您获取地址并将其分配给另一个变量:
mode = & cmode; // I'm not sure how this should actually be done
由于cmode
是一个具有自动生命周期的对象,它会在程序离开创建它的范围时被销毁。结果,存储在mode
中的指针将悬空并指向不再存在的对象。取消引用它会触发未定义的行为。
看起来您的意图是创建具有动态生命周期的对象。 您可以通过以下方式做到这一点:
mode = new DummyMode();
尽管如此,您有责任确保在不再需要对象时将其销毁。
这将通过delete
完成:
delete mode;
在更惯用/现代的 C++ 中,通常使用智能指针来管理此类对象的生命周期,而不是手动调用 delete
。我看到 C++ 的标准库不适用于 Arduino,但似乎确实有一个智能指针可用:https://www.arduino.cc/reference/en/libraries/arxsmartptr/
我建议好好看看使用它来更轻松地避免内存泄漏。
【讨论】:
使用new
关键字就像一个魅力。我将getNext
函数转换为Mode * getNext();
以返回新指针并允许自己销毁前一个实例。有机会我会看看智能指针,但由于这是一个小项目,没有真正的优化需求,这是当时最快的解决方案!谢谢!
智能指针不是对速度的优化,而是对易用性和正确性的优化。以上是关于如何在 Arduino 程序中正确初始化 C++ 对象?的主要内容,如果未能解决你的问题,请参考以下文章
如何初始化静态 arduino (C) 数据结构,然后使用它们?