我的程序存在泄漏问题,涉及到 boost::shared_ptr 拥有的资源。

7

我不知道为什么我的程序会出现内存泄漏,也许你能发现问题所在。

typedef boost::shared_ptr < std::string >   StringPtr;
typedef std::pair < HWND, StringPtr >       WMapPair; 
typedef std::map  < HWND, StringPtr >       WindowMap;

// this callback populates the WindowMap (m_Windows) by adding a WMapPair each time
BOOL CALLBACK EnumWindowsCallback( HWND hWnd )
{
    // adds this window to the WindowMap, along with its title text

    BOOL        bRetVal         = FALSE;
    int         nTextLen        = 0;
    char*       sWindowText     = NULL;     

    if( ! ::IsWindow( hWnd ) )
        return FALSE;

    nTextLen = GetWindowTextLength( hWnd );
    if( ! nTextLen )
        return TRUE;

    sWindowText = new char[nTextLen + 1];
    if( sWindowText )
    {
        GetWindowTextA( hWnd, sWindowText, nTextLen );

        m_Windows.insert( WMapPair(hWnd, StringPtr(new std::string(sWindowText))) );

        delete [] sWindowText;

        sWindowText = NULL;
        bRetVal     = TRUE;
    }

    return bRetVal;
}

我的类包含这个 WindowMap,它的填充工作正常,但是拆卸似乎无法正常工作。类析构函数调用此函数来清除地图 - 这应该释放 shared_ptr,从而删除它们,对吧? :)

void EraseList()
{       
    m_Windows.clear();  
}

我想知道我错过了什么 - 所有的StringPtr都在泄漏。

更新 关于"StringPtr(new std::string(sWindowText))"的风格不正确的评论,我已经按照建议进行了更改,但是内存泄漏仍然存在。

BOOL CALLBACK EnumWindowsCallback( HWND hWnd )
{
    // adds this window to the WindowMap, along with its title text

    BOOL        bRetVal         = FALSE;
    int         nTextLen        = 0;
    char*       sWindowText     = NULL;     
    StringPtr   strPtr;     

    if( ! ::IsWindow( hWnd ) )
        return FALSE;

    nTextLen = GetWindowTextLength( hWnd );
    if( ! nTextLen )
        return TRUE;

    sWindowText = new char[nTextLen + 1];
    if( sWindowText )
    {
        GetWindowTextA( hWnd, sWindowText, nTextLen );

        strPtr = StringPtr(new std::string(sWindowText));

        m_Windows.insert( WMapPair(hWnd, strPtr) );

        delete [] sWindowText;

        sWindowText = NULL;
        bRetVal     = TRUE;
    }

    return bRetVal;
}

结论 我采纳了放弃StringPtr并使用make_pair(hWnd, std::string())的建议,并以此方式规避了这个问题。


3
这段代码风格上存在问题:StringPtr(new std::string(sWindowText))。每个新动态分配的对象都应该最初由一个具有名称的智能指针拥有。要了解更多信息,请阅读Boost shared_ptr 最佳实践。此外,考虑使用 std::vector<char> 代替自己动态分配数组。不过我不认为这两者是您特定问题的原因。 - James McNellis
2
@freefallr:不是的;当你有一个 std::map<int, std::string> m; 并且执行 m.insert(std::make_pair(0, std::string("Hello World"))); 时,一个临时的 std::string("Hello World") 的副本会被插入到 std::map 中。 - James McNellis
1
您确定没有循环引用吗?这就是 boost::weak_ptr 的作用。 - Sam Miller
1
你需要调整向量的大小以确保足够的空间,然后可以使用&v[0]获取指向其初始元素的指针。例如:std::vector<TCHAR> v(nTextLen + 1); GetWindowText(hWnd, &v[0], nTextLen); - James McNellis
4
不行! :-) 你提供的页面是关于operator new的,它是一个你可以重载以为类型进行一些特殊内存分配的运算符。我的页面是关于像new x这样的语句中的new。尽管它们都使用单词new,但它们的意思不同。我可以向你保证,new x不会返回NULL,它会抛出异常。 - Bo Persson
显示剩余17条评论
3个回答

2

在VS2010的std::vector<>实现中存在一个bug,在某些情况下会导致内存泄漏(请参见此处)。据我所知,这个问题已经在VS2010 SP1中得到解决,但我不能百分之百确定。


谢谢提醒,我之前不知道!我已经安装了VS2010 SP1,我的问题似乎与地图有关,而不是向量。 - user206705
没有任何东西阻止 map 在内部使用 vector - Puppy
我可以确认这个问题已经在SP1中得到了修复。 - ildjarn

0

不会回答你的问题(因为我看不到任何问题),但有几点要注意:

typedef std::map  < HWND, std::string >     WindowMap;
typedef WindowMap::value_type               WMapPair;    // In the future it may not be a pair.
                                                         // Or you may change the type of WindowMap
                                                         // Make it so the value is based on the container.
                                                         // Done automatically.

// this callback populates the WindowMap (m_Windows) by adding a WMapPair each time
BOOL CALLBACK EnumWindowsCallback( HWND hWnd )
{
    // adds this window to the WindowMap, along with its title text

    // Declare variables at there first usage point.
    // There is no need to clutter the top of the function 
    // With usless variables that may never be used.

    if( ! ::IsWindow( hWnd ) )
        return FALSE;

    int nTextLen = GetWindowTextLength( hWnd );
    if( ! nTextLen )
        return TRUE;

    // Use a vector. Dynamically allocating memory is dangerious and not
    // exception safe (unless you use a smart pointer (or in this case a container))
    std::vector<char>  sWindowText(nTextLen + 1);
    GetWindowTextA( hWnd, &sWindowText[0], nTextLen );

    // No need for shared pointers. Just put the string in the map.
    m_Windows.insert( WMapPair(hWnd, std::string(sWindowText.begin(),
                                                 sWindowText.begin()+ nTextLen)));

    return TRUE;
}

是的,我采用了这种方法(例如,之前提到的詹姆斯的建议)。 - user206705

0
我已经采纳了James的建议:
@freefallr:不会;当你有一个std::map m;并执行m.insert(std::make_pair(0, std::string("Hello World")));时,临时std::string("Hello World")的副本将被插入到std::map中。- James McNellis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接