Arduino class 层次结构、字符串和内存泄漏
Arduino class hierarchy, Strings and memory leak
下午好,我正在开始一个新的 Arduino 项目 1.6.10 IDE 版本。但是当我使用基于 class 的结构时遇到了一些内存泄漏问题。
我先post我的代码,然后我会指出似乎出现内存泄漏的地方。
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
感谢您的宝贵时间。希望你能帮助我。
免责声明:我想 post 这是评论,而不是答案,因为在我看来它不能解决问题,而只是提供建议。然后我需要一些代码块,所以我需要答案功能。
好吧,恕我直言,您的代码需要一些改进(或者这可能只是因为您减少了它,但无论如何我都会 post 为您改进)
- 不要将函数实现放在头文件中:使用cpp文件存储函数实现,使用头文件存储原型
- 继承的目的是重用你们已经共有的大部分代码。所以有很多不同的变量是没有意义的;最好以不同的方式声明它们。
例如你可以这样使用它:
/* 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
函数 returns 是 void *
而不是 void
。你绝对应该解决这个问题,也许事情会得到解决(事实上你返回的是一个指针而不是什么都没有)。但我不确定。
好像是析构函数的问题,
我post一个基于你的代码的实现..
#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
...
下午好,我正在开始一个新的 Arduino 项目 1.6.10 IDE 版本。但是当我使用基于 class 的结构时遇到了一些内存泄漏问题。
我先post我的代码,然后我会指出似乎出现内存泄漏的地方。
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
感谢您的宝贵时间。希望你能帮助我。
免责声明:我想 post 这是评论,而不是答案,因为在我看来它不能解决问题,而只是提供建议。然后我需要一些代码块,所以我需要答案功能。
好吧,恕我直言,您的代码需要一些改进(或者这可能只是因为您减少了它,但无论如何我都会 post 为您改进)
- 不要将函数实现放在头文件中:使用cpp文件存储函数实现,使用头文件存储原型
- 继承的目的是重用你们已经共有的大部分代码。所以有很多不同的变量是没有意义的;最好以不同的方式声明它们。
例如你可以这样使用它:
/* 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
函数 returns 是 void *
而不是 void
。你绝对应该解决这个问题,也许事情会得到解决(事实上你返回的是一个指针而不是什么都没有)。但我不确定。
好像是析构函数的问题,
我post一个基于你的代码的实现..
#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
...