簡體   English   中英

Arduino類層次結構,字符串和內存泄漏

[英]Arduino class hierarchy, Strings and memory leak

下午好,我要開始一個新的Arduino Project 1.6.10 IDE版本。 但是當我使用基於類的結構時遇到內存泄漏的一些問題。

我先發布代碼,然后在內存泄漏似乎出現的時候指出該位置。

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.hi在評論中指出了重點

#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

我使用庫來跟蹤內存使用情況,這是草圖的輸出:

沒有內存泄漏(字符串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

屬性字符串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

...

免責聲明:我想將其發布為評論,而不是答案,因為我認為這不能解決問題,而只能提供建議。 然后,我需要一些代碼塊,因此我需要答案功能。

好吧,您的代碼恕我直言需要一些改進(或者也許只是因為您減少了它,但是無論如何我都會為您發布它們)

  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;
};

或者,如果您不必更改number的值(因此您不需要實數變量)

/* 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 您絕對應該解決此問題,並且可能所有事情都會得到解決(實際上,您返回的是指針而不是沒有指針)。 但是我不確定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM