std::map导致内存泄漏?

4

编辑

为了让这篇文章更有建设性,可能帮助将来的读者:

问题是这样的:

要解决此问题,请按以下步骤操作:

std::map<Point2, Prop*> mm;
std::pair<Point2, Prop*> p;

if(Keydown(VK_LBUTTON)) {
     p.first = pos; p.second = new Prop();
     mm.insert(p))
}

即使地图会被迭代并在最后释放所有的Prop*指针,但有时插入操作会失败(因为树中可能已经存在具有相同键的pair)。这意味着新创建的Prop()将被孤立。
解决方案: 1) 始终使用std::shared_ptr(可能是最好的解决方案) 2) 或者这样做:
std::map<Point2, Prop*> mm;
std::pair<Point2, Prop*> p;

if(Keydown(VK_LBUTTON)) {
     p.first = pos; p.second = new Prop();
     if(mm.insert(p).second == false) {
          delete p.second;
     }
}

感谢 SO 用户 parapura rajkumar


原始问题:

我的应用程序存在内存泄漏,我不知道是什么导致的!我认为我已经释放了所有需要释放的东西。奇怪的是:每次运行应用程序时并不总是出现内存泄漏。

简单来说,我的应用程序所做的就是:

在初始化时,它会在一个 TileList 内创建 numRowsnumColumnsnew Tile()

当鼠标悬停在屏幕某个位置并按住左键时,它会将一个带有p.second = new Prop()std::pair<Point2,Prop*> p 添加到一个 std::map 中。

有时我可以添加一堆属性然后退出应用程序而没有任何泄漏。有时我会添加与之前相同的属性,但退出时却有内存泄漏。

请帮忙解决。 以下是相关代码:

如果您需要查看特定部分的代码,请评论并编辑问题

PropList.h

class PropList
{
protected:
    std::map<Point2, Prop*> m_Props_m;

public:
    PropList(){}
    virtual ~PropList();

    bool PropAdd(std::pair<Point2, Prop*> p)
    {
        pair<map<Point2, Prop*>::iterator,bool> ret = m_Props_m.insert(p);
        return ret.second;
    }
    bool PropRemove( const Point2& pos );
    bool HasProp( const Point2& pos );

    void Tick();

protected:

};

static void PropRelease(const std::pair<Point2, Prop*>& p) {
    delete p.second;
}

PropList.cpp

PropList::~PropList()
{
    std::for_each(m_Props_m.begin(), m_Props_m.end(), &PropRelease);
}

bool PropList::PropRemove( const Point2& pos )
{
    std::map<Point2, Prop*>::iterator it = m_Props_m.find(pos);
    if (it == m_Props_m.end()) {
        return false;
    }
    delete (*it).second;
    m_Props_m.erase(it);
    return true;
}

TileList.h

class TileList
{
protected:
    std::vector<std::vector<Tile*> > m_Tiles_v;
    PropList m_PropList;

    UINT m_iRowNum;
    UINT m_iColNum;

public:
    TileList(UINT numColumns, UINT numRows);
    virtual ~TileList();

    //Props
    void PropAdd(std::pair<Point2, Prop*> p);
    void PropRemove(const Point2& pos);
    bool HasProp(const Point2& pos);
    void Tick();

    UINT GetNumRows(){return m_iRowNum;}
    UINT GetNumCols(){return m_iColNum;}

protected:
};

TileList.cpp

TileList::TileList(UINT numColumns, UINT numRows)
    :m_iRowNum(numRows)
    ,m_iColNum(numColumns)
{
    for (UINT i = 0; i < numRows; ++i) {
        m_Tiles_v.push_back(std::vector<Tile*>());
        for (UINT j = 0; j < numColumns; ++j) {
            m_Tiles_v[i].push_back(new Tile());
        }
    }
}

TileList::~TileList()
{
    BOOST_FOREACH(std::vector<Tile*> col_tiles_v, m_Tiles_v)
    {
        BOOST_FOREACH(Tile* pTile, col_tiles_v)
        {
            delete pTile;
        }
    }
}

void TileList::PropAdd(std::pair<Point2, Prop*> p)
{
    if(m_PropList.PropAdd(p)) {
        m_Tiles_v[p.first.y][p.first.x]->setOccupied(true);
    }
}

void TileList::PropRemove(const Point2& pos) 
{
    if(m_PropList.PropRemove(pos)) {
        m_Tiles_v[pos.y][pos.x]->setOccupied(false);
    }
}

你尝试过在你的代码上使用valgrind或其他C/C++内存分析器吗?如果有,它告诉了你什么? - kittylyst
2
一旦您开始发现内存问题,您应该使用像Valgrind这样的内存分析工具。 - Oliver Charlesworth
好的,我已经将以下代码添加到我的程序中:#if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); //_crtBreakAlloc= 44685; #endif它告诉我是“Prop”资源出了问题。但是,我仍然不知道具体错在哪里。 - xcrypt
我没有看到任何“新的Prop”,但你至少在两个地方删除了它们。这一切都是关于匹配新闻和删除的。 - AShelly
@Ashelly 这实际上是我的错误,我没有将那部分添加到代码中,它在简短的解释中。 - xcrypt
1个回答

6
我认为你的问题可能出在这里。
 bool PropAdd(std::pair<Point2, Prop*> p)
    {
        pair<map<Point2, Prop*>::iterator,bool> ret = m_Props_m.insert(p);
        return ret.second;
    }

在你的代码中,似乎mProps拥有Prop*。但如果添加了重复的prop,就会发生泄漏,因为唯一的Point2只能存在一个Propmap::insert将失败并使Prop变成孤儿。
每当在map或stl容器中插入对象指针时,请尝试更改为std::shared_ptr以进行自动内存管理。在您的情况下,如果这样做,两个析构函数都不必进行显式清理,您根本不需要使用static PropRelease

1
当您向std::map插入内容时,如果已经存在具有相同值的键,则插入将失败,因此这可能不是问题所在。 - xcrypt
1
@xcrypt 但是原帖中正在分配一个新的Prop并调用PropAdd,假设该属性将被地图删除。插入操作将失败,导致Prop变成孤儿。 - parapura rajkumar
好的!你真是个天才!让我试着修复一下 :) - xcrypt
2
最好的解决方法是使用 shared_ptr。能否至少点个赞呢 :) - parapura rajkumar
我知道,但我目前故意避免使用指针辅助工具,以便亲自动手并获得更多的内存管理经验。但是没错,你是正确的。 - xcrypt

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