从std :: set复制到std :: deque-内存问题

时间:2012-11-29 09:40:42

标签: c++ valgrind

我正在开发一个C ++应用程序,我需要将std :: set的内容复制到std :: deque。类似于我在我的应用程序中所做的事情如下。 (这是简化版)

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

    void UpdateDataSet(std::set<C>& _setData);

private:
    std::deque<C>   deque_Data;//contains ohlc data for the symbol   
};

void A::UpdateDataSet(std::set<C>& _setData)
{
    std::set<C>::iterator itrSet =  _setData.begin();
    std::set<C>::iterator itrSetEnd =  _setData.end();

    while(itrSet != itrSetEnd)
    {
        deque_Data.push_back(*itrSet);
        ++itrSet;
    }
}


int main()
{
    A* pA = new A();

    std::set<C> setData;
    C mC1(5,10);
    C mC2(10,20);
    C mC2(30,40);

    C.insert(mC1);
    C.insert(mC2);
    C.insert(mC3);

    pA->UpdateDataSet(setData);


    return 0;
}

这里C是一个包含两个整数的类。上面的代码工作正常,如我所料。但问题是当我使用valgrind memcheck对我的代码进行分析时,它指出了代码块附近可能丢失的数据

deque_Data.push_back(*itrSet); 

valgrind输出如下。

==3469== 1,032,640 bytes in 180 blocks are possibly lost in loss record 1,337 of 1,346
==3469==    at 0x4006355: operator new(unsigned int) (vg_replace_malloc.c:214)
==3469==    by 0x46B92AF: __gnu_cxx::new_allocator<C*>::allocate(unsigned int, void const*) (new_allocator.h:88)
==3469==    by 0x46B92E7: std::_Deque_base<C, std::allocator<C> >::_M_allocate_map(unsigned int) (stl_deque.h:424)
==3469==    by 0x46B94F9: std::deque<C, std::allocator<C> >::_M_reallocate_map(unsigned int, bool) (deque.tcc:750)
==3469==    by 0x46B960B: std::deque<C, std::allocator<C> >::_M_reserve_map_at_back(unsigned int) (stl_deque.h:1444)
==3469==    by 0x46B96C5: std::deque<C, std::allocator<C> >::_M_push_back_aux(C const&) (deque.tcc:348)
==3469==    by 0x46B9822: std::deque<C, std::allocator<C> >::push_back(C const&) (stl_deque.h:1045)
==3469==    by 0x46B6B09: A::UpdateDataSet(std::set<C, std::less<C>, std::allocator<C> >&)

有人可以帮我查一下内存问题。这个valgrind输出真的意味着内存泄漏吗?

3 个答案:

答案 0 :(得分:2)

你没有在主...的末尾做“删除pA”...

答案 1 :(得分:1)

有两个原因可以解释为什么deque分配的内存可能无法解除分配,假设标准库的正确实现:

  1. 暴力访问deque的内部。使用memset / memcpy或reinterpet_cast / C风格的强制转换可以让您访问deque存储其私有成员的内存位置,例如:分配内存的指针。如果你以任何方式搞乱这些私有成员,deque可能无法释放其动态分配的内存,从而导致memleaks。通常情况下,这种激进行为会导致违反多种行为的行为,因此您会发现相当于崩溃的事情而不仅仅是回忆。
  2. 缺少deque析构函数调用。如果出于任何原因没有调用deque的析构函数,它就没有机会“清理”,即释放其拥有的动态内存,导致所述memleaks。在您的情况下,deque析构函数会自动被A的析构函数调用,除非您有deque*并在免费存储/堆上分配了deque本身(见下文)。缺少原因A的析构函数的调用可能是以下一个或多个:
    • 缺少删除:delete调用对象的析构函数并释放对象所在的内存。在这种情况下,您应该看到另一个memleak,指向分配和创建对象的位置(在示例代码中为新A()
    • 调用free()而不是delete:free只释放内存但不调用任何析构函数。永远不要匹配新的和免费的,甚至更好的尝试不使用malloc / free与C ++对象,除非你非常小心并知道你做了什么(放置新...)
    • “跳出”代码。我以某种方式“跳出”范围,编译器可能无法在范围块的末尾执行自动析构函数调用。这不适用于例外,因为编译器知道该做什么以及如何清理。这个应该不适用于goto,因为如果你用goto违反了对象的生命周期边界,大多数编译器会发出错误。尽管如此,尽量不要在C ++中使用goto,因为您可以使用其他功能。我可以在这里发送的唯一“跳跃”是longjmp,这是一个不尊重范围边界的C功能,因此忽略了对析构函数调用的需求。 从不在C ++代码中使用longjmp
  3. 如果有人能想到更多有关memleak的理由,缺少析构函数或其他人,请给我一个提示,我会编辑这个答案: - )

    PS:我刚看到我错过了“看下面”的部分 - 如果你在A中有一个deque*而不是deque,那该怎么办?在这种情况下,A负责正确构建和破坏双端队列,这意味着   - 无论何时需要创建队列,都要调用new   - 至少在析构函数中调用delete   - 正确处理复制/移动分配和复制/移动构造 但是,如果您有一个deque*并搞砸了指针处理,您应该看到另一个memleak,指向您将拥有的任何new deque个电话。

答案 2 :(得分:1)

您的示例中缺少某些内容,没有内存泄漏。

我遵守并运行了此代码

 #include <set>
 #include <deque>

 class C
 {
 public:
     C (int a, int b)
         :   m_a (a)
         ,   m_b (b)
     {
     }

     bool operator <(const C& c) const
     {
         return m_a < c.m_a || (m_a == c.m_a && m_b < c.m_b);
     }

 private:
     int m_a;
     int m_b;
 };
 class A
 {
     public:
         A(){};
         ~A() {};

         void UpdateDataSet(std::set<C>& _setData);

     private:
         std::deque<C>   deque_Data;//contains ohlc data for the symbol
 };

 void A::UpdateDataSet(std::set<C>& _setData)
 {
     std::set<C>::iterator itrSet =  _setData.begin();
     std::set<C>::iterator itrSetEnd =  _setData.end();

     while(itrSet != itrSetEnd)
     {
         deque_Data.push_back(*itrSet);
         ++itrSet;
     }
 }


 int main()
 {
     A* pA = new A();

     std::set<C> setData;
     C mC1(5,10);
     C mC2(10,20);
     C mC3(30,40);

     setData.insert(mC1);
     setData.insert(mC2);
     setData.insert(mC3);

     pA->UpdateDataSet(setData);
     delete pA;


     return 0;
 }

valgrind没有按预期报告任何可疑的内容