使用std :: list <std :: string> </std :: string>时,std :: string会导致内存泄漏

时间:2010-08-07 01:13:52

标签: c++ string list memory-leaks

我正在当前项目中使用std::list<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;
    }

}

为什么我的字符串列表会导致内存泄漏?不应该删除列表导致在每个包含的字符串上调用析构函数吗?

5 个答案:

答案 0 :(得分:30)

在第一种情况下,list类不知道您使用new分配了字符串,并且无法删除它。特别是,该列表只包含您传入的字符串的副本。

同样,在第二种情况下,您永远不会释放行对象s,从而泄漏内存。删除内部字符串的原因是您没有正确实现复制构造函数。因此,如果你复制一个Line对象,它们都会引用相同的字符串指针,如果你试图删除它们,那么你就麻烦了。

答案 1 :(得分:13)

你的Line类需要一个copy-ctor和一个正确处理字符串指针的赋值运算符。

或者,只需要一个std::string成员而不是指针,让string类处理内存(这就是它的用途)。

答案 2 :(得分: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的指针成员。

答案 3 :(得分: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类提供复制构造函数和赋值运算符。

答案 4 :(得分: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还使用引用计数来提升共享指针和弱指针,以了解何时释放被指向的对象。使用共享指针,指针的每个副本都会增加引用计数,并且只要所有共享指针都超出范围,就会删除指向的对象。弱指针指向由共享指针管理的对象,但不会增加引用计数,如果删除所有父共享指针,尝试取消引用弱指针将引发容易捕获的异常。

显然智能指针的范围更广,但我强烈建议将它们看作是帮助管理记忆的解决方案。