Добрый день, я начинаю новый проект 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
Спасибо за ваше время. Надеюсь, ты сможешь мне помочь.
похоже, проблема деструктора,
Я размещаю реализацию на основе вашего кода ..
#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
...
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я хотел опубликовать это как комментарий, а не как ответ, потому что, по моему мнению, это не решает проблему, а просто дает советы. Затем мне понадобилось несколько блоков кода, поэтому мне нужны были функции ответа.
Ну, ваш код ИМХО нуждается в некоторых улучшениях (или, может быть, это только потому, что вы сократили его, но в любом случае я выложу их для вас)
Например, вы можете использовать это так:
/* 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
, Вы должны определенно это исправить, и МОЖЕТ быть исправлено (на самом деле вы возвращаете указатель вместо ничего). Но я не уверен в этом.