Destruction of an object when erasing it from std::map

c++, erase, std, stdmap

Solution

`std::map` stores a copy of the object you insert. When the object is removed, it is this copy which is destructed. So after `m[1] = t1;`, there are two identical instances of `CTestMap`: `t1` and the one in the map.

Also: `m[1] = t1;` will first create a new entry in the map, using the default constructor, and later assign `t1` to it.

In general, if you want to trace instance lifetime like this, you need provide a user defined copy constructor and assignment operator which trace as well. And you probably want to output the `this` pointer in all of the traces. (Another technique would be to dote each object with an immutable unique identifier:

#define TRACE(m) std::cout << #m << '(' << m_objectId << ')' << std::endl
static int currentObjectId = 0;

class TestMap
{
    int m_id;
    int const m_objectId;
public:
    TestMap()
        : m_id( 0 )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(DFLT);
    }
    TestMap( int id )
        : m_id( id )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(CTOR);
    }
    TestMap( TestMap const& other )
        : m_id( other.m_id )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(COPY);
    }
    ~TestMap()
    {
        TRACE(DTOR);
    }
    TestMap& operator=( TestMap const& other )
    {
        m_id = other.m_id;
        TRACE(ASGN);
        return *this;
    }
};

You might want to add additional information (like `m_id`) to the trace as well.

Also: your last output invokes undefined behavior. After `m.find(i)`, you should check first that the iterator hasn't returned `m.end()`. If it has, dereferencing isn't allowed. So your test output should be something like:

void
testOutput( std::map<int, TestMap> const& m, int i )
{
    std::map<int, TestMap>::const_iterator entry = m.find( i );
    if ( entry == m.end() ) {
        std::cout << "no object at " << i << std::endl;
    } else {
        std::out << "object " << entry->second.m_id << " at " << i << std::endl;
    }
}

(Finally: I think Microsoft has preempted the `C` prefix for classes, so you should avoid it. If you want a prefix, choose something else, to avoid confusion.)

Problem

I was curios if default destructor is called, when I'm removing an element from an `std::map`. Here is an example which I have made: ``` class CTestMap{ public: CTestMap() { std::cout << "default constructor called" << std::endl; } CTestMap(int id) { std::cout << "created object: " << id << std::endl; m_id = id; } ~CTestMap() { std::cout << "destroyed object: " << this->m_id << std::endl; } int get_id(){ return m_id; } int m_id; }; int main(void){ std::map<int, CTestMap>m; std::map<int, CTestMap>::iterator m_it; std::cout << "created map " << std::endl; CTestMap t1(1); std::cout << "created test object: " << t1.get_id() << std::endl; CTestMap t2(2); std::cout << "created test object: " << t2.get_id() << std::endl; CTestMap t3(3); std::cout << "created test object: " << t3.get_id() << std::endl; m[1] = t1; m_it = m.find(1); std::cout << "inserted test object: " << m_it->second.get_id() << std::endl; m[2] = t2; m_it = m.find(2); std::cout << "inserted test object: " << m_it->second.get_id() << std::endl; m[3] = t3; m_it = m.find(3); std::cout << "inserted test object: " << m_it->second.get_id() << std::endl; m_it = m.find(1); std::cout << "will now erased test object: " << m_it->second.get_id() << std::endl; m.erase(m.find(1)); std::cout << "erased test object: " << m[1].get_id() << std::endl; m_it = m.find(1); std::cout << "object shall no longer exist: " << m_it->second.get_id() << std::endl; while(1); return 0; } ``` An here is the output: ``` ./htest created map created object: 1 created test object: 1 created object: 2 created test object: 2 created object: 3 created test object: 3 default constructor called destroyed object: 9377935 destroyed object: 9377935 inserted test object: 1 default constructor called destroyed object: 9377935 destroyed object: 9377935 inserted test object: 2 default constructor called destroyed object: 9377935 destroyed object: 9377935 inserted test object: 3 will now erased test object: 1 destroyed object: 1 default constructor called destroyed object: 158830600 destroyed object: 158830600 erased test object: 158830600 object shall no longer exist: 158830600 ``` Questions are: - Why so many times default constructor is called, when I'm only creating 3 objects using my own constructor ? - Can I, based on this example say, that each time I'm erasing any object from the `std::map`, its destructor is called ? Is this general behavior of a `std::map` ? I could not find this information. - What if I'm storing pointers to objects (I'm creating them using 'new' operator) ? When then `delete` shall be called ?

Original source