使用map时出现内存泄漏问题

5

我无法清空地图内存(通过Valgrind检查过)。

#include <map>

class testMap {
    public:
        testMap(){}
        ~testMap();

        void insert_map(int, int);

    private:
          std::map<int,int> _map;
};

void testMap::insert_map(int i, int j){
    _map.insert( pair<int, int>(i,j));
}

我尝试过 _map.clear(), erase(), 手动删除 _map->second,但仍然没有成功。


感谢所有的回复。实际上,map本身并不是问题,但是与单例模式一起使用会导致泄漏。下面的代码有什么问题?

#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include "Object.h"

#include<boost/smart_ptr.hpp>

using namespace std;

class Singleton {
    public:

        // A wrapper around Object class
        class object
        {
            public:
                object() : _object(new Object())
                    {}
                Object get(void)
                    { return _object.get(); }
            private:
                boost::shared_ptr<Object> _object;
        };

        object insert_new(const std::string key)
        {
            _object_maps.insert( pair<string,object>( key, object() ));
            return _object_maps.find( key )->second;
            //_test_object = object();
            //return _test_object;  // Leak goes away if I don't use map.
        }

        static Singleton* Instance();
        void Print();

    protected:
        Singleton(){}
        ~Singleton();

    private:
        static Singleton* _instance;

        std::map<std::string, object > _object_maps;
        object _test_object;
};

Singleton* Singleton::_instance = 0;

Singleton* Singleton::Instance() {
    if( _instance ==0 )
    {
        _instance = new Singleton();
    }
    return _instance;
}

void Singleton::Print() {
    std::cout << " Hi I am a singleton object" << std::endl;
}

Singleton::~Singleton()
{
    _object_maps.clear();
}

我从另一个代码中调用了以下内容:

    Singleton::object _test_object(Singleton::Instance()->insert_new("TEST"));

有问题吗?我遇到了一个Valgrind错误,像这样:

      ==19584== 17 bytes in 1 blocks are possibly lost in loss record 31,429 of 52,291
      ==19584==    at 0x69A1642: operator new(unsigned int) (vg_replace_malloc.c:255)
      ==19584==    by 0x772CB0A: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.8)
      ==19584==    by 0x772D904: ??? (in /usr/lib/libstdc++.so.6.0.8)
       ==19584==    by 0x772DB16: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.8)
       ==19584==    by 0xBF1BC17: test::test() (test.C:34)
       ==19584==    by 0xBF1DB66: G__testDict_143_0_1(G__value*, char const*, G__param*, int) (testDict.C:190)
       ==19584==    by 0x70EA4E5: Cint::G__ExceptionWrapper(int (*)(G__value*, char const*, G__param*, int), G__value*, char*, G__param*, int) (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71EF2E4: G__call_cppfunc (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71C0095: G__interpret_func (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71AF883: G__getfunction (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71D8CC1: G__new_operator (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x718D07F: G__getexpr (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x717724E: G__define_var (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71FDEC6: G__defined_type (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x7201A6D: G__exec_statement (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71BF6C8: G__interpret_func (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x71AF62F: G__getfunction (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x718437D: G__getitem (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x7189F12: G__getexpr (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)
       ==19584==    by 0x719713F: G__calc_internal (in /afs/rhic.bnl.gov/@sys/opt/phenix/root-5.17.01/lib/libCint.so)

2
你从valgrind得到了什么输出? - bdonlan
2
你能否展示一下创建/销毁testMap对象的调用代码? - Suroot
1
还有,你的地图名称前面加下划线?不好!!! - George
2
@George,我也是这么认为,直到有人告诉我实际规则——在类的范围内是可以的。我认为这是成员变量的一个相当普遍的约定。 - Mark Ransom
@Mark:有了那样的态度,你怎么希望在网络上获得“网络权威”的声誉呢? :-) - Nemo
显示剩余4条评论
2个回答

5

简短回答:
你显式声明了析构函数,但是没有定义它(忘记加 {} 了)。

详细回答:

  1. Your code does not even compile. Missing {} in class destructor and std:: in front of pair.
  2. Corrected and completed with main:

    #include <map>
    
    class testMap {
      public:
        testMap() {}
        ~testMap() {};
    
        void insert_map(int, int);
    
      private:
        std::map<int,int> _map;
    };
    
    void testMap::insert_map(int i, int j) {
        _map.insert(std::pair<int, int>(i,j));
    }
    
    int main() {
        testMap t;
        t.insert_map(12, 34);
        return 0;
    }
    
  3. Compiled on 32-bit Ubuntu 11.04:

    g++ leak.cpp -o leak
    
  4. Run under valgrind supervision:

    valgrind ./leak
    ==20773== Memcheck, a memory error detector
    ==20773== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
    ==20773== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
    ==20773== Command: ./leak
    ==20773== 
    ==20773== 
    ==20773== HEAP SUMMARY:
    ==20773==     in use at exit: 0 bytes in 0 blocks
    ==20773==   total heap usage: 1 allocs, 1 frees, 24 bytes allocated
    ==20773== 
    ==20773== All heap blocks were freed -- no leaks are possible
    ==20773== 
    ==20773== For counts of detected and suppressed errors, rerun with: -v
    ==20773== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 6)
    
  5. No memory leakage.

也许你的编译器会自动定义一个完全空的类析构函数(因为缺少 {} ),不再在退出时自动调用私有成员映射析构函数。
希望对你有所帮助 :)

我很好奇看到长答案,因为我不知道它除了生成链接器错误之外还会有什么伤害。 - Mark Ransom
@Mark:我认为这里的重点是在语法错误存在时编译器的奇怪行为。我刚问了一下楼主他使用的编译器是什么。 - Vanni Totaro
@Vanni:如果用户已经声明了析构函数,编译器自动生成析构函数的话,我会感到震惊...应该是链接器错误。但是,+1 说明在合理的编译器下,map 的一般用法是正确的。 - Tony Delroy

3

尝试:

{
    std::map<int,int> empty_map;
    empty_map.swap(_map);
}

(至少,这是说服标准库容器实际释放其内存的常规方法。)

4
更简洁地说,std::map<int, int>().swap(_map);。该语句的作用是交换一个空map和另一个map对象 _map 的内容,以清空 _map 中的所有元素。 - ildjarn
1
@ildjarn:你的代码有一个优点,就是可以立即释放内存,而不是等到empty_map超出作用域。我的代码则更容易被新手理解。"简洁"并不总是"更好"。 - Nemo
2
我从来没有说“更好”,有原因的。 - ildjarn

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