Arduino классовая иерархия, строки и утечка памяти

Добрый день, я начинаю новый проект Arduino 1.6.10 IDE ver. но я сталкиваюсь с некоторыми проблемами утечки памяти при использовании структуры на основе классов.

Сначала я публикую свой код, а затем укажу место, где, кажется, появляется утечка памяти.

mainSketchFile.

#include <Ethernet.h>
#include <MemoryFree.h>
#include "Constants.h"#include "State.h"StateFactory CurrentStateFactory;

void setup() {

pinMode(BUZZER,OUTPUT);
Serial.begin(9600);
Serial.println("START");
delay(1000);

}

void loop() {

Serial.print(F("Free RAM = "));
Serial.println(freeMemory(), DEC);  // print how much RAM is available.
CurrentStateFactory.changeStatus(1);
Serial.println(CurrentStateFactory.getCurrentState()->getNumber());
CurrentStateFactory.changeStatus(2);
Serial.println(CurrentStateFactory.getCurrentState()->getNumber());
}

Проблема, кажется, в State.h я отметил точку в комментариях

#ifndef State_h
#define State_h

/////////////////// STATE/////////////////////////

class MachineState{
public:
virtual int getNumber();
protected:

};

/////////////////////ACTIVE FULL/////////////////////////////////
class ActiveFull : public MachineState
{
public:
ActiveFull();
virtual int getNumber();
private:
String statusName; //<----- PROBLRM SEEMS TO BE HERE WHEN COMMENTED NO MEMORY LEAK APPEN
int number;
};

ActiveFull::ActiveFull(){
this->number=1;
};int ActiveFull::getNumber(){
return this->number;
}

////////////////////////////// ACTIVE EMPTY ////////////////////
class ActiveEmpty : public MachineState
{
public:
ActiveEmpty();
virtual int getNumber();
protected:
String statusName;//<----- PROBLRM SEEMS TO BE HERE WHEN COMMENTED NO MEMORY LEAK APPEN
int number;
};

ActiveEmpty::ActiveEmpty(){
this->number=2;
};

int ActiveEmpty::getNumber(){
return this->number;
}//////////////////FACTORY/////////////////////////////class StateFactory{
private:
MachineState *currentState;
public:
StateFactory();
void *changeStatus(int choice); // factory
MachineState *getCurrentState();
};

StateFactory::StateFactory(){
MachineState *var1=new ActiveFull();
this->currentState=var1;
}

MachineState *StateFactory::getCurrentState(){
return this->currentState;
}void *StateFactory::changeStatus(int choice)
{
delete  this->currentState;  // to prevent memory leak
if (choice == 1){
MachineState *var1=new ActiveFull();
this->currentState=var1;
}
else if (choice == 2){
MachineState *var1=new ActiveEmpty;
this->currentState=var1;
}
else{
MachineState *var1=new ActiveEmpty;
this->currentState=var1;
}
}

#endif

я использую библиотеку для отслеживания использования памяти, и это результат скетча:

Нет утечки памяти (String statusName прокомментировано)

Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2
Free RAM = 7897
1
2

Утечка памяти, когда свойство String statusName не закомментировано

Free RAM = 6567
1
2
Free RAM = 6559
1
2
Free RAM = 6551
1
2
Free RAM = 6543
1
2
Free RAM = 6535
1
2
Free RAM = 6527
1
2

Спасибо за ваше время. Надеюсь, ты сможешь мне помочь.

0

Решение

похоже, проблема деструктора,

Я размещаю реализацию на основе вашего кода ..

#ifndef State_h
#define State_h/* MachineState Class */
class MachineState{
public:
virtual void test() = 0;
MachineState(){
number = 0;
statusName = "NULL";
}
virtual ~MachineState(){
Serial.println("Destroy base");
}
void setNumber(int n){
number =  n;
}
void setStatusName(String some){
statusName = some;
}
String getStatusName(){
return statusName;
}
int getNumber(){
return number;
}
virtual void print()const{
Serial.println("Class MS");
}
protected:
String statusName;
int number;

};/* ActiveFull Class */
class ActiveFull : public MachineState{
public:
ActiveFull(){
x = "Class AF";
setNumber(1);
}
void print()const{
Serial.println("Class AF");
}
void test(){}
~ActiveFull(){
Serial.println("Destroy AF");
}
private:
String x;
};/* ActiveEmpty Class */
class ActiveEmpty : public MachineState
{
public:
void print()const{
Serial.println("Class EE");
}
ActiveEmpty(){
x = "Class EE";
setNumber(2);
}
void test(){}
~ActiveEmpty(){
Serial.println("Destroy EE");
}
private:
String x;
};

/* StateFactory Class */
class StateFactory{
private:
MachineState *currentState;
public:
StateFactory();
~StateFactory(){
Serial.println("Ho distrutto StateFactory");
}
void changeStatus(int choice); // factory
MachineState *getCurrentState();
};

StateFactory::StateFactory(){
this->currentState=new ActiveFull();
}

MachineState *StateFactory::getCurrentState(){
return this->currentState;
}void StateFactory::changeStatus(int choice){
if(this->currenState)
delete  this->currentState;
if (choice == 1){
currentState = new ActiveFull();
}
else if (choice == 2){
currentState = new ActiveEmpty();
}
else{
currentState = new ActiveEmpty();
}
}

#endif

Это мой результат с вашим главным:

...

2
Class EE
Free RAM = 7751
Destroy EE
Destroy base
1
Class AF
Destroy AF
Destroy base
2
Class EE
Free RAM = 7751
Destroy EE
Destroy base
1
Class AF
Destroy AF
Destroy base

...
1

Другие решения

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я хотел опубликовать это как комментарий, а не как ответ, потому что, по моему мнению, это не решает проблему, а просто дает советы. Затем мне понадобилось несколько блоков кода, поэтому мне нужны были функции ответа.

Ну, ваш код ИМХО нуждается в некоторых улучшениях (или, может быть, это только потому, что вы сократили его, но в любом случае я выложу их для вас)

  1. Не помещайте реализацию функции в заголовочный файл: используйте файл cpp для хранения реализации функций и файл заголовка для хранения прототипов
  2. Целью наследования является повторное использование большей части общего кода, который у вас уже есть. Поэтому бессмысленно иметь много разных переменных; намного лучше объявить их по-другому.

Например, вы можете использовать это так:

/* File State.h */

class MachineState{
public:
int getNumber();
protected:
String statusName;
int number;
};

/////////////////////ACTIVE FULL/////////////////////////////////
class ActiveFull : public MachineState
{
public:
ActiveFull();
};

////////////////////////////// ACTIVE EMPTY ////////////////////
class ActiveEmpty : public MachineState
{
public:
ActiveEmpty();
};

/* File State.cpp */

int MachineState::getNumber(){
return this->number;
}

ActiveEmpty::ActiveEmpty(){
this->number=1;
};

ActiveEmpty::ActiveEmpty(){
this->number=2;
};

или, если вам не нужно менять значение числа (и вам не нужна настоящая переменная)

/* File State.h */

class MachineState{
public:
virtual int getNumber() = 0;
protected:
String statusName;
};

/////////////////////ACTIVE FULL/////////////////////////////////
class ActiveFull : public MachineState
{
public:
virtual int getNumber();
};

////////////////////////////// ACTIVE EMPTY ////////////////////
class ActiveEmpty : public MachineState
{
public:
virtual int getNumber();
};

/* File State.cpp */

int ActiveEmpty::getNumber(){
return 1;
};

int ActiveEmpty::getNumber(){
return 2;
};

Тогда есть небольшая проблема с освобождением: если new не получится, у вас возникнут проблемы на следующем delete, Чтобы решить эту проблему, вы можете сделать что-то вроде (и я также немного сократил ваш код)

void *StateFactory::changeStatus(int choice)
{
if (this->currentState) // If it was correctly allocated
delete this->currentState;  // to prevent memory leak
switch (choice)
{
case 1:
this->currentState = new ActiveFull();
break;
case 2: // case 2 can be removed since it is identical to default
this->currentState = new ActiveEmpty();
break;
default:
this->currentState = new ActiveEmpty();
break;
}
}

Тем не менее, я бы изменил цикл следующим образом:

void printCurrentStateNumber()
{
if (CurrentStateFactory.getCurrentState())
Serial.println(CurrentStateFactory.getCurrentState()->getNumber());
else
Serial.println("No more memory");
}

void loop() {
Serial.print(F("Free RAM = "));
Serial.println(freeMemory(), DEC);  // print how much RAM is available.
CurrentStateFactory.changeStatus(1);
printCurrentStateNumber();
CurrentStateFactory.changeStatus(2);
printCurrentStateNumber();
}

Это для проверки, если состояние было успешно создано.

Что касается вашей явной проблемы, я не знаю, как работает библиотечная функция. Прежде чем начать понимать причину этой утечки, я попытаюсь выяснить, действительно ли это утечка. Так что запустите измененную программу (с тестом перед удалением и печатью строки памяти больше нет) и дайте ей поработать, пока библиотека не сообщит вам, что ей не хватает памяти. Если он стабилизируется или достигает 0 и не печатает это, это проблема библиотеки. С другой стороны, если программа останавливает печать строки, это утечка.

Одно замечание: нехорошая привычка позволять небольшому микроконтроллеру выполнять распределения и освобождения слишком часто, поскольку он имеет ограниченную память. Проведите тест, потому что, если есть реальная утечка, возможно, ее следует исследовать больше, но для вашего приложения я предлагаю вам подумать о постоянном выделении двух экземпляров объекта, а затем просто использовать их в соответствии со значением, которое вы передали ранее — очевидно, если есть только пара производных классов), вот так:

/* In the header file */
#define NUM_OF_STATES 2

class StateFactory{
private:
MachineState states[NUM_OF_STATES];
public:
StateFactory();
void changeStatus(int choice); // factory
MachineState *getCurrentState();
private:
int currentIdx;
};

/* In the source file */

StateFactory::StateFactory()
{
states[0] = new ActiveFull();
states[1] = new ActiveEmpty();
this->currentIdx = 0;
}

MachineState *StateFactory::getCurrentState(){
return states[this->currentIdx];
}

void StateFactory::changeStatus(int choice)
{
switch (choice)
{
case 1:
this->currentIdx = 0;
break;
case 2: // case 2 can be removed since it is identical to default
this->currentIdx = 1;
break;
default:
this->currentIdx = 1;
break;
}
}

ЗАКЛЮЧИТЕЛЬНОЕ ПРИМЕЧАНИЕ: пересматривая ответ, я обнаружил, что ваш changeStatus функция возвращает void * вместо void, Вы должны определенно это исправить, и МОЖЕТ быть исправлено (на самом деле вы возвращаете указатель вместо ничего). Но я не уверен в этом.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector