tags:

views:

1647

answers:

5

Hello,

I am observing strange behaviour of std::map::clear(). This method is supposed to call element's destructor when called, however memory is still accessible after call to clear().

For example:

struct A
{
  ~A() { x = 0; }
  int x;
};

int main( void )
{
  std::map< int, A * > my_map;
  A *a = new A();
  a->x = 5;
  my_map.insert( std::make_pair< int, *A >( 0, a ) );

  // addresses will be the same, will print 5
  std::cout << a << " " << my_map[0] << " " << my_map[0]->x << std::endl;

  my_map.clear();

  // will be 0
  std::cout << a->x << std::endl;

  return 0;
}

The question is, why is variable a still accessible after its destructor was called by map::clear()? Do I need to write delete a; after calling my_map.clear() or is it safe to overwrite the contents of a?

Thanks in advance for your help, sneg

+13  A: 

If you store pointers on a map (or a list, or anything like that) YOU are the responsible for deleting the pointers, since the map doesn't know if they have been created with new, or not. The clear function only invokes destructors if you don't use pointers.

Oh, and one more thing: invoking a destructor (or even calling delete) doesn't mean the memory can't be accessed anymore. It only means that you will be accessing garbage if you do.

Marc
+3  A: 

That's because map.clear() calls destructors of the data contained in the map, in your case, of the pointer to a. And this does nothing.

You might want to put some kind of smart pointer in the map for the memory occupied by a to be automatically reclaimed.

BTW, why do you put the template arguments in the call to make_pair? The template argument deduction should do pretty well here.

jpalecek
So if I don't use any sort of smart pointer classes, do I need go through the map and manually `delete` every single element? How do I clear the map after that? I mean, `clear()` will attempt to call destructors of elements that were already deleted.
sneg
Just delete all of your pointers, and then call clear(). As jpalecek told you, calling the destructor of a pointer (which is totally different from calling the desructor of the object it is pointing to) does nothing.
Marc
Just to clarify: if you store actual objects in a map, its destructor will be called. But if you store pointers (any kind of pointer), the map will only know that it has a bunch of meaningless pointers, and will treat them as what they essentially are: integers representing memory addresses.
Marc
Thanks, Marc. I understand now.In my code snippet above destructor of `struct A` _does_ get called. I.e. element `x` is set to 0. I was worried that if I delete this instance of `struct A` map.clear() will attempt to call destructor again.
sneg
sneg-vx: This is strange. I tried it with gcc 4.3 and got 5 two times in the output, so the destructor wasn't called (as the standard says).
jpalecek
Actually, you are right. It was something strange I encountered. I just tested this on VS2008 and gcc 4.1.1 and destructor isn't called. Thanks again for your help.
sneg
+8  A: 

std::map does not manage the memory pointed to by the pointer values - it's up to you to do it yourself. If you don't want to use smart pointers, you can write a general purpose free & clear function like this:

template <typename M> void FreeClear( M & amap ) 
    for ( typename M::iterator it = amap.begin(); it != amap.end(); ++it ) {
        delete it->second;
    }
    amap.clear();
}

And use it:

std::map< int, A * > my_map;
// populate
FreeClear( my_map )

;

anon
I suggest adding a link to a question about smart pointers and/or to boost::shared_ptr.
MadKeithV
A: 

When you free a piece of heap memory, its contents don't get zeroed. They are merely available for allocation again. Of course you should consider the memory non accessible, because the effects of accessing unallocated memory are undefined.

Actually preventing access to a memory page happens on a lower level, and std libraries don't do that.

When you allocate memory with new, you need to delete it yourself, unless you use a smart pointer.

abababa22
A: 

adding to the content:

any container stores your object Type and call corresponding constructors: internal code each node might look similar to :

__NodePtr { *next; __Ty Val; }

when you allocate it happens by constructing the val based on type and then linking. some thing similar to

_Ty _Val = _Ty(); _Myhead = _Buynode(); _Construct_n(_Count, _Val);

when you delete it calls corresponding destructors.

when you store references(pointers) it wont call any constructor nor it will destruct.

FL4SOF