2014-03-19 46 views
1

我的程序是使用SDL庫中的類編寫的。刪除堆上存儲數據的堆上的對象

我有下面的類:

class s_group 
{ 
    private: 
     SDL_Surface* image; 
     unsigned int* F_total; 
     float* F_length; 
     SDL_Rect** F; 
     float* F_current; 
     unsigned int S_total; 
     unsigned int S_current; 

    public: 
     s_group(void); 
     virtual ~s_group(void); 

     bool setup(const char* filename, unsigned int s); 
     //other member functions 
}; 

私人構件指針每個商店堆上宣佈存儲器中的位置,由所述成員函數setup爲分配。

bool s_group::setup(const char* filename, unsigned int s) 
{ 
    s_group::~s_group();//delete already allocated heap memory 
    if(!load_file(image, filename)) 
    { 
     image = NULL; 
     return false; 
    } 

    S_total = s; 
    F = new SDL_Rect*[S_total]; 
    F_total = new unsigned int[S_total]; 
    F_length = new float[S_total]; 
    F_current = new float[S_total]; 

    for(unsigned int index = 0; index < S_total; ++index) 
    { 
     F[index] = NULL; 
     F_total[index] = 0; 
     F_length[index] = 0.f; 
     F_current[index] = 0.f; 
    } 
    //loop for each array slot and set values of data 
    return true; 
} 

在一個大的功能我在堆上創建這個類的一個對象,存儲其地址在名爲sparkles_group指針。

s_group* sparkle = new s_group; 
sparkle->setup("sparkle_final.png", 1); 

在功能上完成我叫delete重新分配堆內存。刪除這條線解決了這個問題,然而這會導致內存泄漏。

delete sparkle; 
sparkle = NULL; 

這將調用這是我認爲發生錯誤的類的析構函數由於內部使用了delete運營商。

s_group::~s_group(void) 
{ 
    SDL_FreeSurface(image); 
    image = NULL; 

    for(unsigned int s = 0; s < S_total; ++s) 
    { 
     for(unsigned int f = 0; f < F_total[s]; ++f) 
     { 
      F[s][f].x = 0; 
      F[s][f].y = 0; 
      F[s][f].w = 0; 
      F[s][f].h = 0; 
     } 
     delete[] F[s]; 
     F[s] = NULL; 
    } 
    delete[] F; 
    F = NULL; 

    delete[] F_total; 
    F_total = NULL; 

    delete[] F_length; 
    F_length = NULL; 

    delete[] F_current; 
    F_current = NULL; 

    S_total = 0; 
    S_current = 0; 
} 

在到達delete運算符,會出現一個對話框,指出:

Windows已經引發了Program.exe文件斷點。這可能是由於堆的損壞,這表明Program.exe或它已加載的任何DLL中存在一個錯誤。

我該如何delete這個對象不會導致堆損壞?

+1

永遠不要調用你自己的析構函數那樣..我只見過這用於分配器.. – Brandon

+0

儘管我不能給你一個完整的答案,因爲我現在無法測試你的代碼。我現在可以告訴你,你永遠不應該直接調用析構函數。把代碼放在一個單獨的私有方法中可以在設置方法和析構函數中調用它。另外,在調用delete之前檢查一個變量是否爲空。現在,如果您在不調用「setup」方法的情況下刪除對象,則代碼將崩潰。最後,儘量避免單獨的「初始化」或「設置」方法,使用構造函數來初始化類。 –

+0

'我如何刪除這個對象而不會導致堆損壞? '如果您使用C++容器和/或智能指針,那麼堆損壞將大大減少,或者完全消除。在C++的這個時代,用戶類不需要像你所做的那樣編碼。 – PaulMcKenzie

回答

0

自發布此問題以來,我找到了錯誤的來源並解決了問題。 在爲動態2D數組設置數據值的單獨代碼段中,循環驗證不正確。

for(unsigned int index = 0; index <= F_total[ S_current ]; ++index) { 
    //set data values for each slot in the array 
    F[ S_current ][ index ].x = 0; etc... 
} 

可以看出,循環顯然會嘗試修改一個等於創建數組大小的位置。當然,注意到數組從索引0開始,因此最終的插槽將大小爲-1。寫代碼時我錯過了一些非常愚蠢的事情。實際的循環:

for(unsigned int index = 0; index < F_total[ S_current ]; ++index) { 
    //set data values for each slot in the array 
    F[ S_current ][ index ].x = 0; etc... 
} 

任何人試圖自己的內存管理的消息:

  • 查找堆損壞的來源是困難的,因爲編譯器將在代碼段不必然導致定位錯誤問題。
  • 問題的原因將只會在影響內存的代碼部分出現。確保您不會嘗試訪問或更糟地修改您未提供的任何內存。
  • 我仍然認爲內存管理是一種很好的學習方式,並且比使用容器或智能指針更適合以這種方式完成任何項目。這是我個人的偏好,儘管定製內存管理通常只提供很少的優勢,只有複雜性。
  • 尋求幫助時,請提供有關該問題的所有相關代碼。雖然編譯器可能會在一節中指導您解決問題,但正如我之前所說的,堆損壞並不一定就存在。
2

effective C++ Scott Meyers

項目9:不要調用施工或破壞過程中虛函數。

你不應該在施工或破壞時調用虛函數,因爲調用不會做你的想法,如果他們這樣做,你仍然會不高興。如果您是恢復Java或C#程序員,請密切關注此項目,因爲這是一個使用C++ zags的語言縮寫的地方。

其實,即使你應該定義你的析構函數,調用它強行應該是出了問題

0

我無法編譯代碼,但在這裏不用..

的第一件事我注意到你稱爲你的析構函數。你不想那麼做!相反,創建一個釋放函數並調用它。

我注意到接下來的事情是,有類本身內沒有FRAME變量..所以這條線:

FRAME = new SDL_Rect*[S_total];

將會導致編譯錯誤,您的析構函數使用FRAME但沒有這樣的變量存在。我覺得你的意思將其更改爲F因爲如果不是,那麼這行:

F[index] = NULL;

是不確定的行爲,因爲F是未初始化..

而且,你永遠不會初始化FRAME等各項指標訪問它的析構等:

FRAME[s][f].x = 0;

是一個沒有沒有。

同樣,你叫

delete[] F; 
F = NULL; 

但F有沒有分配的內存未初始化。

與所有補丁

因此我認爲:

class s_group 
{ 
    private: 
     SDL_Surface* image; 
     unsigned int* F_total; 
     float* F_length; 
     SDL_Rect** FRAME; 
     float* F_current; 
     unsigned int S_total; 
     unsigned int S_current; 

     void Release(); 

    public: 
     s_group(void); 
     virtual ~s_group(void); 

     bool setup(const char* filename, unsigned int s); 
     //other member functions 
}; 


bool s_group::setup(const char* filename, unsigned int s) 
{ 
    Release();//delete already allocated heap memory 

    if(!load_file(image, filename)) 
    { 
     image = NULL; 
     return false; 
    } 

    S_total = s; 
    FRAME = new SDL_Rect*[S_total]; 
    F_total = new unsigned int[S_total]; 
    F_length = new float[S_total]; 
    F_current = new float[S_total]; 

    for(unsigned int index = 0; index < S_total; ++index) 
    { 
     FRAME[index] = NULL; 
     F_total[index] = 0; 
     F_length[index] = 0.f; 
     F_current[index] = 0.f; 
    } 
    //loop for each array slot and set values of data 
    return true; 
} 

void s_group::Release() 
{ 
    SDL_FreeSurface(image); 
    image = NULL; 

    for(unsigned int s = 0; s < S_total; ++s) 
    { 
     for(unsigned int f = 0; f < F_total[s]; ++f) 
     { 
      if (FRAME[s]) 
      { 
       FRAME[s][f].x = 0; 
       FRAME[s][f].y = 0; 
       FRAME[s][f].w = 0; 
       FRAME[s][f].h = 0; 
      } 
     } 
     delete[] FRAME[s]; 
     FRAME[s] = NULL; 
    } 
    delete[] FRAME; 
    FRAME = NULL; 

    delete[] F_total; 
    F_total = NULL; 

    delete[] F_length; 
    F_length = NULL; 

    delete[] F_current; 
    F_current = NULL; 

    S_total = 0; 
    S_current = 0; 
} 

s_group::~s_group(void) 
{ 
    Release(); 
} 

應該這樣做..但不要忘記分配內存FRAME[index]我不知道有多少或什麼你想分配,所以我改變檢查Release功能,如果FRAME[index]有效與if-statement

我會強烈建議你使用一些SmartPointers,而忘記了自己的處理每一個內存分配..

+0

當我複製代碼時,所有對'FRAME'的引用都改爲'F',所以這不成問題。 –

+1

@ user3438091如果上面的代碼不是您測試過的代碼來顯示問題,那麼肯定會出現問題。 – Yakk

+0

@Yakk我只更改了標識符,因此問題不太具體。 –