如何在 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/ 我建议好好看看如何使用它来更轻松地避免内存泄漏。