Anupama Pathirage
Anupama Pathirage

Reputation: 691

Copy from std::set to std::deque- Memory issue

I'm developing a C++ application and there I need to copy the contents of a std::set to a std::deque. Something similar to what I'm doing in my application is as follows. ( This is a simplified version)

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;
}

Here C is a Class which contains two integers. The above code working fine as I expected. But the problem is when I do a profiling on my code using valgrind memcheck, it points out some possibly lost data near the code block

deque_Data.push_back(*itrSet); 

The valgrind output is as below.

==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> >&)

Can someone please help me to find out the memory issue here. Is this valgrind output really mean a memory leak?

Upvotes: 0

Views: 914

Answers (3)

Arne Mertz
Arne Mertz

Reputation: 24626

There are two reasons I can think of why the memory allocated by a deque might not get deallocated, assuming a correct implementation of your standard library:

  1. Violently accessing the deque's internals. Using memset/memcpy or reinterpet_cast/C-style casts might give you access to memory locations where the deque stores its private members, e.g. pointers to allocated memory. If you mess around with those private members in any way, deque might not be able to deallocate its dynamically allocated memory, leading to memleaks. Normally such agressive acts lead to access violations of many kinds, so you would observe rather crashes than just memleaks.
  2. Missing deque destructor call. If for any reason a deque's destructor gets not called, it has no opportunity to "clean up", i.e. deallocate its owned dynamic memory, leading to said memleaks. In Your case, the deque destructor automatically gets called by A's destructor, unless you have a deque* and allocated the deque itself on the free store/heap (see below). Reason for a missing call of A's destructor might be one or more of the following:
    • Missing delete: delete calls the destructor of an object and releases the memory where the object had been. In that case, you should see another memleak, pointing where the object was allocated and created (in your sample code the new A())
    • Calling free() instead of delete: free only frees the memory but does not call any destructors. Never match new and free, even better try not to use malloc/free with C++ objects unless you are very careful and know what you do (placement new...)
    • "Jumping" out of code. I you somehow "jump" out of scopes, the compiler might not be able to do the automated destructor calls at the end of the scope blocks. This does not apply for exceptions, because compilers know what to do and how to clean up. This should not apply for goto's, since most compilers emit errors if you violate object lifetime boundaries with goto. Nevertheless, try not to use goto in C++, as there are other features you can use. The only "jumping" i could emagine here is longjmp, which is a C feature that does not respect scope boundaries and thus ignores the need for destructor calls, amongst others. Never use longjmp in C++ code.

If anybody can think of more reasons for the memleak, missing destructor calls or others, give me a hint, I'll edit this answer :-)

PS: I just saw I missed the "see below" part - so what's the deal if you have a deque* in A instead of a deque? In that case, A is responsible for correctly constructing and destructing the deque, meaning - call new whenever you need to create the queue - call delete at least in the destructor - handle especially copy/move assignment and copy/move construction correctly However, if you have a deque* and messed up the pointer handling you should see another memleak, pointing you to any of the new deque calls you will have.

Upvotes: 1

user1773602
user1773602

Reputation:

You are missing something in your example, there is no memory leak.

I complied and ran this code

 #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 doesn't report anything suspicious, as expected

Upvotes: 1

Jan R&#252;egg
Jan R&#252;egg

Reputation: 10075

You didn't do a "delete pA" at the end of main...

Upvotes: 2

Related Questions