使用C++字符串可能发生内存泄漏

pr1*_*268 10 c++ string memory-leaks

考虑以下C++程序:

#include <cstdlib> // for exit(3)
#include <string>
#include <iostream>
using namespace std;

void die()
{
    exit(0);
}

int main()
{
    string s("Hello, World!");
    cout << s << endl;
    die();
}
Run Code Online (Sandbox Code Playgroud)

通过valgrind运行这个显示了这个(一些输出为了简洁而修剪):

==1643== HEAP SUMMARY:
==1643==     in use at exit: 26 bytes in 1 blocks
==1643==   total heap usage: 1 allocs, 0 frees, 26 bytes allocated
==1643==
==1643== LEAK SUMMARY:
==1643==    definitely lost: 0 bytes in 0 blocks
==1643==    indirectly lost: 0 bytes in 0 blocks
==1643==      possibly lost: 26 bytes in 1 blocks
==1643==    still reachable: 0 bytes in 0 blocks
==1643==         suppressed: 0 bytes in 0 blocks
Run Code Online (Sandbox Code Playgroud)

如您所见,堆上分配的26个字节可能会丢失.我知道这个std::string类有一个12字节的结构(至少在我的32位x86 arch和GNU编译器4.2.4上)和"Hello,World!" 使用null终止符有14个字节.如果我理解正确,12字节结构包含指向字符串的指针,分配的大小和引用计数(如果我在这里错了,有人会纠正我).

现在我的问题是:如何在堆栈/堆中存储C++字符串?声明时是否存在std::string(或其他STL容器)的堆栈对象?

PS我在某处读过valgrind 可能会报告某些使用STL容器(以及"几乎容器"等std::string)的C++程序中的内存泄漏误报.我并不太担心这种泄漏,但它确实激起了我对STL容器和内存管理的好奇心.

GMa*_*ckG 11

调用exit"终止程序而不离开当前块,因此不会破坏任何具有自动存储持续时间的对象".

换句话说,泄漏与否,你应该不在乎.当你打电话时exit,你说"关闭这个程序,我不再关心其中的任何内容." 所以停止关怀.:)

显然它会泄漏资源,因为你永远不会让字符串的析构函数运行,绝对不管它如何管理这些资源.


jmn*_*nas 9

其他人是正确的,你正在泄漏,因为你正在呼叫退出.要清楚,泄漏不是堆栈上分配的字符串,而是字符串在堆上分配的内存.例如:

struct Foo { };

int main()
{
    Foo f;
    die();
}
Run Code Online (Sandbox Code Playgroud)

不会导致valgrind报告泄漏.

泄漏是可能的(而不是确定的),因为你有一个指向堆上分配的内存的内部指针.basic_string负责这一点.从我的机器上的标题:

   *  A string looks like this:
   *
   *  @code
   *                                        [_Rep]
   *                                        _M_length
   *   [basic_string<char_type>]            _M_capacity
   *   _M_dataplus                          _M_refcount
   *   _M_p ---------------->               unnamed array of char_type
   *  @endcode
   *
   *  Where the _M_p points to the first character in the string, and
   *  you cast it to a pointer-to-_Rep and subtract 1 to get a
   *  pointer to the header.
Run Code Online (Sandbox Code Playgroud)

它们的关键是_M_p不指向堆上分配的内存的开始,它指向字符串中的第一个字符.这是一个简单的例子:

struct Foo
{
    Foo()
    {
        // Allocate 4 ints.
        m_data = new int[4];
        // Move the pointer.
        ++m_data;
        // Null the pointer
        //m_data = 0;
    }
    ~Foo()
    {
        // Put the pointer back, then delete it.
        --m_data;
        delete [] m_data;
    }
    int* m_data;
};

int main()
{
    Foo f;
    die();
}
Run Code Online (Sandbox Code Playgroud)

这将报告valgrind可能发生的泄漏.如果你注释掉我移动的行m_data,valgrind将报告'仍然可以访问'.如果取消注释我将m_data设置为0的行,您将获得明确的泄漏.

valgrind 文档提供了有关可能泄漏和内部指针的更多信息.