2010-08-07 94 views
16

我在當前項目中使用std::list<std::string>。但是有一處與此相關的內存泄漏。所以我已經單獨測試了有問題的代碼:使用std :: list時使用std :: string的內存泄漏<std::string>

#include <iostream> 
#include <string> 
#include <list> 

class Line { 
public: 
    Line(); 
    ~Line(); 
    std::string* mString; 
}; 

Line::Line() { 
    mString = new std::string("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 
} 

Line::~Line() { 
    //mString->clear(); // should not be neccessary 
    delete mString; 
} 

int main(int argc, char** argv) 
{ 
    // no memory leak 
    while (1==1) { 
     std::string *test = new std::string("XXXXXXXXXXXXXXXXXXXXXXXX"); 
     delete test; 
    } 

    // LEAK! 
    // This causes a memory overflow, because the string thats added 
    // to the list is not deleted when the list is deleted. 
    while (1==1) { 
     std::list<std::string> *sl = new std::list<std::string>; 
     std::string *s = new std::string("XXXXXXXXXXXXXXXXXXXXXXX"); 
     sl->push_back(*s); 
     //sl->pop_back(); //doesn't delete the string?- just the pointer 
     delete sl; 
    } 

    // LEAK! 
    // Here the string IS deleted, but the memory does still fill up 
    // but slower 
    while (1==1) { 
     std::list<Line> *sl = new std::list<Line>; 
     Line *s = new Line(); 
     sl->push_back(*s); 
     //sl->pop_back(); //does delete the Line-Element 
     sl->clear(); 
     delete sl; 
    } 
    return 0; 

    // this does not cause any noticable memory leak 
    while (1==1) { 
     std::list<int> *sl = new std::list<int>; 
     int i = 0xFFFF; 
     sl->push_back(i); 
     sl->clear(); 
     delete sl; 
    } 
    return 0; 

    // This does not cause any overflow or leak 
    while (1==1) { 
     int *i; 
     i= new int [9999]; 
     delete[] i; 
    } 

} 

爲什麼我的字符串列表導致內存泄漏?不應該刪除列表導致每個包含的字符串上調用析構函數?

+338

停止使用'new'這麼多。我看不出任何你在任何地方使用新的理由。您可以在C++中按值創建對象,這是使用該語言的巨大優勢之一。您不必分配堆中的所有內容。不要像Java程序員那樣思考。 – Omnifarious 2010-08-07 01:20:07

回答

26

在第一種情況下,list類不知道您分配了字符串new,並且無法刪除它。特別是,該列表只包含您通過的字符串的副本。

同樣,在第二種情況下,您從不釋放行對象s,因此泄漏內存。內部字符串被刪除的原因是因爲您沒有正確實施複製構造函數。因此,如果您製作Line對象的副本,它們都將引用相同的字符串指針,並且如果您嘗試刪除它們,則會遇到問題。

7

這是你的泄漏:

while (1==1) { 
    std::list<Line> *sl = new std::list<Line>; 
    Line *s = new Line(); 
    sl->push_back(*s); 
    //sl->pop_back(); //does delete the Line-Element 
    sl->clear(); 
    delete sl; 
} 

STL集合店要素按值,分配和它釋放的空間。你分配必須明確釋放。只需在循環結尾添加delete s即可。

如果你的必須存儲指針,考慮存儲託管指針如boost::shared_ptr,或者查看Boost pointer container library

第二次看,你根本不需要在堆上分配Line。只需將其更改爲:

sl->push_back(Line()); 

而且,正如其他人指出,確保Line的指針成員在拷貝構造適當的管理,複製,分配和析構函數。

2
std::list<Line> *sl = new std::list<Line>; 
    Line *s = new Line(); 
    sl->push_back(*s); 
    //sl->pop_back(); //does delete the Line-Element 
    sl->clear(); 
    delete sl; 

您忘記了刪除s。您新建了它,您必須將其刪除。由於您在管理Line類中的內存的同時複製對象(將它們填充到列表中),因此還必須爲Line類提供複製構造函數和賦值運算符。

12

您的Line類需要一個copy-ctor和一個正確處理字符串指針的賦值操作符。

或者,只需要一個std::string成員而不是指針,並讓string類處理內存(就是這麼做的)。

2

其他人已具體解決了爲什麼你有你的泄漏 - 刪除指針列表不刪除指向的對象,並不應該作爲一個簡單的指針沒有指示是否是唯一的引用該對象),但有更多的方法比確保迭代刪除列表來刪除指針。


至於這裏的例子說明世界上沒有其他原因,當他們離開的範圍,使用指針到任何東西,因爲你使用他們時,他們進入的範圍和丟棄它們 - 只需在創造一切相反,編譯器會正確地處理退出範圍的所有內容。例如。

while (1==1) { 
    std::list<std::string> sl; 
    std::string s = std::string("XXXXXXXXXXXXXXXXXXXXXXX"); 
    sl.push_back(s); 
} 

如果你確實需要的指針行爲(以避免重複由很多事情掛鉤對象等等,等等),你應該看一看智能指針,因爲這將消除許多因爲它們可以自動處理所需的引用計數和語義。 (具體來說take a look at the boost smart pointers

智能指針有多種類型可以根據具體需要和所有權語義來表示。

std :: auto_ptr擁有嚴格的所有權 - 如果指針被「複製」,則原始值被清零且所有權轉移 - 只有一個有效的auto_ptr指向對象。只要具有所有權的智能指針超出範圍,指向的對象就會被刪除。

Theres還使用引用計數來提升共享指針和弱指針,以知道何時釋放指向的對象。使用Shared指針時,指針的每個副本都會增加引用計數,並且每當所有共享指針超出作用域時,指向的對象都會被刪除。弱指針指向由共享指針管理的對象,但如果所有父共享指針被刪除,則不會增加引用計數,試圖解引用弱指針會引發易於捕獲的異常。

很明顯,智能指針的範圍更多,但我強烈建議看看它們作爲幫助管理內存的解決方案。

相關問題