简体   繁体   English

使用std :: list时std :: string的内存泄漏<std::string>

[英]Memory leak with std::string when using std::list<std::string>

I'm working with std::list<std::string> in my current project. 我正在当前项目中使用std::list<std::string> But there is a memory leak somewhere connected with this. 但是有一个与此相关的内存泄漏。 So I've tested the problematic code separately: 所以我分别测试了有问题的代码:

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

}

Why does my string list cause a memory leak? 为什么我的字符串列表会导致内存泄漏? Shouldn't deleting the list cause the destructors to be called on each contained string? 不应该删除列表导致在每个包含的字符串上调用析构函数?

In the first case, the list class has no idea you allocated the string with new , and cannot delete it. 在第一种情况下, list类不知道您使用new分配了字符串,并且无法删除它。 In particular, the list only ever contains a copy of the string that you passed in. 特别是,该列表只包含您传入的字符串的副本。

Similarly, in the second case, you never free the line object s , and thus you leak memory. 类似地,在第二种情况下,你从未释放线对象s ,因此你泄漏内存。 The reason why the internal string is deleted is because you have not correctly implemented a copy constructor. 删除内部字符串的原因是您没有正确实现复制构造函数。 Thus, if you make a copy of a Line object, both of them will reference the same string pointer, and if you try to delete both of them, you are in trouble. 因此,如果您复制一个Line对象,它们都将引用相同的字符串指针,如果您尝试删除它们,则会遇到麻烦。

Your Line class needs a copy-ctor and an assignment operator that properly deal with the string pointer. Line类需要一个copy-ctor和一个正确处理字符串指针的赋值运算符。

Alternatively, just have a std::string member rather than a pointer and let the string class handle the memory (that's what it's for). 或者,只有一个std::string成员而不是指针,让string类处理内存(这就是它的用途)。

Here's your leak: 这是你的泄漏:

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 collections store elements by value , allocating and releasing space for it. STL集合按值存储元素,为其分配和释放空间。 What you allocated you have to release explicitly. 分配的内容必须明确发布。 Just add delete s to the end of the loop. 只需将delete s添加到循环的末尾即可。

If you have to store pointers, consider storing managed pointers like boost::shared_ptr , or look into Boost pointer container library . 如果你必须存储指针,考虑存储像boost::shared_ptr这样的托管指针,或者查看Boost指针容器库

On the second look, you don't need to allocate Line on the heap at all. 在第二种情况下,您根本不需要在堆上分配Line Just change it to: 只需将其更改为:

sl->push_back(Line());

And, as others noted, make sure Line 's pointer members are properly managed in copy-constructor, copy-assignment, and destructor. 并且,正如其他人所指出的那样,确保在复制构造函数,复制赋值和析构函数中正确管理Line的指针成员。

    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;

You forgot to delete s . 你忘了删除s You new'ed it, you have to delete it. 你新来了,你必须删除它。 As you're copying objects around(By stuffing them in a list) while managing memory in your Line class, you also have to provide a copy constructor and assignment operator for your Line class. 当您在Line类中管理内存时复制对象(通过将它们填入列表中),您还必须为Line类提供复制构造函数和赋值运算符。

Others have addressed specifically why you have your leak - deleting a list of pointers does not delete the objects that are pointed to, and should not as a simple pointer gives no indication whether it was the only reference to that object ), but there are more ways than having to make sure you iterate the list on deletion to delete the pointers. 其他人已经具体解决了为什么你有泄漏 - 删除指针列表不会删除指向的对象,并且不应该作为一个简单的指针不指示它是否是对该对象的唯一引用,但还有更多除了必须确保在删除时迭代列表以删除指针。


As far as the example here shows theres no reason to use pointers to anything at all, since you're using them when they enter the scope and discarding them when they leave the scope - simply create everything on the stack instead and the compiler will properly dispose of everything on exiting the scopes. 至于这里的例子显示没有理由使用指针到任何东西,因为你在进入范围时使用它们并在它们离开范围时丢弃它们 - 只需在堆栈上创建所有内容而编译器将正确在退出范围时处理所有内容。 Eg. 例如。

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

If you do need the pointer behaviour (to avoid having to duplicate objects that are linked to by many things etc. etc.) you should take a look at smart pointers, as these will remove many of the pitfalls as they can automatically handle the reference counting and semantics you require. 如果你确实需要指针行为(为了避免重复链接到许多东西等的对象等),你应该看看智能指针,因为这些将消除许多陷阱,因为他们可以自动处理参考你需要的计数和语义。 (Specifically take a look at the boost smart pointers ) (具体看一下boost智能指针

There are many types of smart pointer you can use depending on specific need and ownership semantic to represent. 您可以使用多种类型的智能指针,具体取决于要表示的特定需求和所有权语义。

The std::auto_ptr has strict ownership - if the pointer is "copied" the original is nulled and ownership transfered - there is only ever be one valid auto_ptr to the object. std :: auto_ptr具有严格的所有权 - 如果指针被“复制”,则原件被清空并且所有权被转移 - 对象只有一个有效的auto_ptr。 The object pointed to is deleted whenever the smart pointer with ownership goes out of scope. 只要具有所有权的智能指针超出范围,就会删除指向的对象。

Theres also boost shared pointers and weak pointers using reference counting to know when to free the object being pointed to. 还使用引用计数来提升共享指针和弱指针,以了解何时释放被指向的对象。 With Shared pointers each copy of the pointer increases a reference count, and the object pointed to is deleted whenever all the shared pointers go out of scope. 使用共享指针,指针的每个副本都会增加引用计数,并且只要所有共享指针都超出范围,就会删除指向的对象。 A weak pointer points to an object managed by a shared pointer but does not increase the reference count, if all the parent shared pointers are deleted attempting to dereference a weak pointer will throw an easily catchable exception. 弱指针指向由共享指针管理的对象但不增加引用计数,如果删除所有父共享指针,尝试取消引用弱指针将引发容易捕获的异常。

Obviously theres a lot more to the range of smart pointers, but I highly suggest taking a look at them as a solution to help with managing your memory. 显然智能指针的范围更广,但我强烈建议将它们看作是帮助管理记忆的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM