如何在 Arduino 程序中正确初始化 C++ 对象?
How can I properly initialize C++ objects within an Arduino program?
在我的简单 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 cmode = DummyMode();
您正在创建一个 DummyMode
具有自动生命周期(通常称为在堆栈上)的实例 cmode
。
然后您获取地址并将其分配给另一个变量:
mode = & cmode; // I'm not sure how this should actually be done
由于 cmode
是一个具有自动生命周期的对象,当程序离开创建它的范围时,它将被销毁。结果,存储在 mode
中的指针将悬空并指向一个不再存在的对象。取消引用它会触发未定义的行为。
看起来你的意图是创建具有动态生命周期的对象。
您可以通过以下方式做到这一点:
mode = new DummyMode();
不过您有责任确保对象在不再需要时被销毁。
这将通过 delete
:
完成
delete mode;
在更多 idiomatic/modern C++ 中,通常使用智能指针来管理此类对象的生命周期,而不是手动调用 delete
。我看到 C++ 的标准库不适用于 Arduino,但似乎有一个智能指针可用:https://www.arduino.cc/reference/en/libraries/arxsmartptr/
我建议好好看看如何使用它来更轻松地避免内存泄漏。
在我的简单 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 cmode = DummyMode();
您正在创建一个 DummyMode
具有自动生命周期(通常称为在堆栈上)的实例 cmode
。
然后您获取地址并将其分配给另一个变量:
mode = & cmode; // I'm not sure how this should actually be done
由于 cmode
是一个具有自动生命周期的对象,当程序离开创建它的范围时,它将被销毁。结果,存储在 mode
中的指针将悬空并指向一个不再存在的对象。取消引用它会触发未定义的行为。
看起来你的意图是创建具有动态生命周期的对象。 您可以通过以下方式做到这一点:
mode = new DummyMode();
不过您有责任确保对象在不再需要时被销毁。
这将通过 delete
:
delete mode;
在更多 idiomatic/modern C++ 中,通常使用智能指针来管理此类对象的生命周期,而不是手动调用 delete
。我看到 C++ 的标准库不适用于 Arduino,但似乎有一个智能指针可用:https://www.arduino.cc/reference/en/libraries/arxsmartptr/
我建议好好看看如何使用它来更轻松地避免内存泄漏。