【发布时间】:2020-11-13 07:55:02
【问题描述】:
对不起,我对 C++ 很陌生,我正在编写一个 Arduino 草图。
我想实现一种面向命令的方法,其中我有一个命令队列、一个父虚拟/抽象类 Command,并且每个实际命令都继承自该类。
为了实现这个队列,我使用了我从 Arduino 的库管理器(某种“图书馆商店”)获得的 LinkedList 库。所以我有:
LinkedList<Command*> queue = new LinkedList<Command*>();
我添加了 * 因为否则它不会编译。
问题是我正在使用“new”运算符将命令推送到队列。如果我只使用列表中的一个对象,它可以正常工作,但目的是在队列中有多个命令,问题是如果我更改实例的成员,它将更改所有其他实例也在队列中,就好像它们都指向同一个实例一样。当然,变量没有声明为静态的。
我怀疑是因为列表初始化中的*,但说实话,我还没有完全理解C++指针,这只是一个疯狂的猜测。
更新 #1:回答 molbdnilo 请求。这是最小的可重现示例(您需要 Luis Llamas 创建的 LinkedListLib 库,可以从 Arduino Library Manager 获得):
#include <LinkedListLib.h>
// Definiciones
class Command {
private:
String state = "NOT_EXEC"; // Estados: NOT_EXEC, NOT_FINISHED, FINISHED
public:
virtual void init() = 0; // Función de inicialización de comando
virtual void exec() = 0; // Función de ejecución del comando
virtual void finish() = 0; // Función de finalizado del comando
void setStatus(String s){ state = s;};
virtual ~Command(){};
String getStatus(){
return state;
} // Entrega el estado del comando
};
class Parpadear : public Command{
private:
long t;
int c = 0;
int stat;
int mil;
int rep;
public:
Parpadear(int x, int y){
mil = x;
rep = y;
}
void init() override{
t = millis();
stat = HIGH;
digitalWrite(LED_BUILTIN, stat);
}
void exec() override{
if (millis() - t > mil){
stat = stat == HIGH ? LOW : HIGH;
digitalWrite(LED_BUILTIN, stat);
t = millis();
c++;
}
if(c == rep){
setStatus("FINISHED");
}
}
void finish() override{
}
Parpadear::~Parpadear(){
}
};
// Estados: 1 = init, 2 = exec, 3 = main
const int INIT = 1; // Initialization state
const int EXEC = 2; // Command execution state
const int MAIN = 3; // Main loop code state
int state = INIT;
long init_time;
LinkedList<Command*> queue = LinkedList<Command*>();
String commandResult;
void setup() {
// Inicialización de Arduino
pinMode(LED_BUILTIN, OUTPUT); // Configura LED en tabla
Serial.begin(9600);
}
void loop() {
// Máquina de estados para control global de la estructura orientada a comandos
switch(state){
case INIT:
// Inicialización de la máquina de estados
init_time = millis(); // Obtener el tiempo en el que la máquina de estados se empezó a ejecutar por primera vez.
state = EXEC; // Realizar transición a ejecución de comandos.
queue.InsertHead(new Parpadear(2000, 4));
queue.InsertTail(new Parpadear(300, 50));
break;
case EXEC:
// Ejecución de comandos en cola
if(queue.GetSize() > 0){
if(queue.GetHead()->getStatus() == "NOT_EXEC"){
queue.GetHead()->init();
queue.GetHead()->setStatus("NOT_FINISHED");
}
queue.GetHead()->exec(); // Ejecuta primer comando en la cola
if(queue.GetHead()->getStatus() == "FINISHED"){
//Serial.println(queue.GetHead()->getStatus());
queue.GetHead()->finish();
queue.RemoveAt(0); // Quitar el primer comando de la lista
}
}
break;
case MAIN:
break;
}
}
更新 #2:关于 Botje 的问题:
我如何确定这会改变两个实例?
(请参考switch case EXEC,代码参考)
从父Command类继承的所有Commands(在这种情况下,只实现了Parpadear类),都有一个名为“state”的String变量存储命令的状态,所以每次一个命令排队时,它只有当它完成执行时,这个“状态”变量才会将其值更改为“FINISHED”(否则它将是“NOT_FINISHED”),这会触发队列从列表中删除命令。
使用 Arduino 的串行监视器工具,我可以看到在排队 2 个命令后,第一个命令将相应地执行,但是一旦第一个被删除,第二个也被立即删除。我使用 Serial.println 为每个命令打印“状态”变量,当另一个不应该时,所有这些都导致“完成”。
还打印了变量“c”,它只是一个计数器,在这两种情况下,它都等于相同的值 (4),因为在第二种情况下,“c”甚至还没有被操纵。
【问题讨论】:
-
请阅读minimal reproducible example,然后创建一个。
-
您如何确定队列中的所有其他实例也发生了变化?受此影响的代码在哪里?
-
感谢大家的回答,我更新了问题以解决您的问题。再次感谢大家!
标签: c++ oop c++11 arduino linked-list