Arduino类层次结构,字符串和内存泄漏

时间:2016-12-21 15:32:50

标签: c++ string memory-leaks arduino class-hierarchy

下午好,我正在开始一个新的Arduino Project 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

感谢您为时间提供建议。希望你能帮帮我。

2 个答案:

答案 0 :(得分:1)

它似乎是一个析构函数的问题,

我根据您的代码发布了一个实现..

#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 :(得分:0)

免责声明:我想将此作为评论而不是答案发布,因为在我看来,它并没有解决问题,只是给出了建议。然后我需要一些代码块,所以我需要答案功能。

嗯,你的代码恕我直言需要一些改进(或者只是因为你减少了它,但无论如何我会为你发布它们)

  1. 不要将函数实现放在头文件中:使用cpp文件存储函数实现,使用头文件存储原型
  2. 继承的目的是重用您已经拥有的大部分代码。因此,拥有许多不同的变量是没有意义的;更好地宣布它们不同。
  3. 例如,您可以像这样使用它:

    /* 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。你应该肯定解决这个问题,并且可能会修复一些东西(实际上你正在返回指针而不是任何东西)。但我对此并不确定。