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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3428750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:49:12  来源:igfitidea点击:

Memory leak with std::string when using std::list<std::string>

c++stringlistmemory-leaks

提问by Rock

I'm working with std::list<std::string>in my current project. But there is a memory leak somewhere connected with this. So I've tested the problematic code separately:

我正在与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;
    }

}

Why does my string list cause a memory leak? Shouldn't deleting the list cause the destructors to be called on each contained string?

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

回答by grddev

In the first case, the listclass has no idea you allocated the string with new, and cannot delete it. In particular, the list only ever contains a copy of the string that you passed in.

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

Similarly, in the second case, you never free the line object s, and thus you leak memory. 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 Lineobject, both of them will reference the same string pointer, and if you try to delete both of them, you are in trouble.

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

回答by Michael Burr

Your Line class needs a copy-ctor and an assignment operator that properly deal with the string pointer.

您的 Line 类需要一个复制构造函数和一个正确处理字符串指针的赋值运算符。

Alternatively, just have a std::stringmember rather than a pointer and let the stringclass handle the memory (that's what it's for).

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

回答by Nikolai Fetissov

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. What youallocated youhave to release explicitly. Just add delete sto the end of the loop.

STL 集合按值存储元素,为其分配和释放空间。什么分配必须明确地释放。只需添加delete s到循环的末尾。

If you have tostore 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 Lineon the heap at all. Just change it to:

再看一下,您根本不需要Line在堆上进行分配。只需将其更改为:

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在复制构造函数、复制赋值和析构函数中正确管理 的指针成员。

回答by nos

    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. 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.

你忘记删除了s。你新建了它,你必须删除它。当您在 Line 类中管理内存的同时复制对象(通过将它们填充到列表中)时,您还必须为 Line 类提供复制构造函数和赋值运算符。

回答by Fish

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. The object pointed to is deleted whenever the smart pointer with ownership goes out of scope.

std::auto_ptr 具有严格的所有权——如果指针被“复制”,则原始指针为空并转移所有权——对象永远只有一个有效的 auto_ptr。只要具有所有权的智能指针超出范围,所指向的对象就会被删除。

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.

显然,智能指针的范围还有很多,但我强烈建议将它们视为帮助管理内存的解决方案。