STL and the map template
Don Freeman

Ok...I've not really ever messed with the STL map template, so I may be doing this wrong...

My problem is that the destructor for Tile is NOT being called! I have read the description for map, but it does not call the destructor(s) as it should for some reason!

1#include <map>
2using std::map;
3using std::pair;
4enum TerrainTypes
5{
6 Terrain_Plains,
7 Terrain_Forest,
8};
9class Tile
10{
11public:
12 Tile()
13 {
14 type = Terrain_Plains;
15 printf("Tile created.\n");
16 }
17 ~Tile()
18 {
19 printf("Tile destroyed.\n");
20 }
21 enum TerrainType type;
22 BITMAP *image;
23};
24class Map
25{
26public:
27 Map()
28 {
29 Tile *pTile = new Tile;
30 pTile->type = Terrain_Forest;
31 tiles.insert ( pair<int,Tile*>(0,pTile) );
32 printf("size of tiles<int,Tile*>: %i\n",tiles.size());
33 tiles.clear();
34 printf("After CLEAR()...size of tiles<int,Tile*>: %i\n",tiles.size());
35 }
36 ~Map()
37 {
38 tiles.clear();
39 }
40 map<int,Tile*> tiles;
41};

Probably something I've done that is stupid....hehehe ::)

As always...Thanks in advance,
Donald

BAF

IIRC, clear just clears the map, not calling the destructor or anything. You will have to iterate through the map yourself, deleting each pointer stored.

Don Freeman
map-C++ Reference said:

Clear content

All the elements in the container are dropped: their destructors are called, and then they are removed from the container, leaving it with a size of 0.

Maybe I am just confused as to what/who's destructor clear is calling????

BAF

A pointer doesn't have a deconstructor though. If they were just tiles (not tile*s) then it would work as you are expecting.

Don Freeman

So...the clear function does not dereference the pointers (as in this case?)

How would I add elements to the map without having to worry about them going out of scope?

...
void SomeFunction( map<int,Tile> &tiles )
{
   Tile aTile;
   tiles.insert(pair<int,Tile>0,aTile);
}
...

Would that be acceptable? I thought that since this would go out of scope, the map class would not contain valid data anymore...or is it that it is calling a copy constructor for the temporary?

[Edit 1]
Ok...I tryed this, it is ok...but calls the copy constructor...how do you avoid that? Using references/pointers....which is where I am at in the beginning... :(
[/Edit]

X-G

That's right. If you had been storing objects instead of pointers, the destructors would've been called; but the way you've done it now, you need to go through it.

EDIT: As for your reply there, yes, that's valid. There'll be a copy of the object in the map.

Don Freeman

Thanks to all...

I get confused with pointers every once in a while for some stupid reason.... I don't know why I let them confuse me so... ::)

Working insert without calling copy constructor:

  Tile pTile;
  pTile.type = Terrain_Forest;
  tiles.insert ( pair<int,Tile*>(0,&pTile) );
  printf("size of tiles<int,Tile*>: %i\n",tiles.size());
  tiles.clear();
  printf("After CLEAR()...size of tiles<int,Tile*>: %i\n",tiles.size());

X-G

Uh... that's not a good idea. That's an access violation waiting to happen.

BAF

That's going to give you invalid pointers as soon as pTile goes out of scope. The easiest solution, IMO, would be to do what you were doing before and just to iterate the map and delete each object.

Don Freeman

I was hoping for some kind of "automatic" cleanup when the map object goes out of scope, but I guess I'll have to manually delete all the objects....damn!

Again, Thanks to X-G and BAF,
Donald

X-G

There is such a thing: store objects instead of pointers. :P

Don Freeman

Ok then...two part question...

One...how would I do THAT (store objects instead of pointers)?
Two...would this be correct (from code before):

  map<int,Tile*>::iterator it;
  for ( it=tiles.begin(); it != tiles.end(); it++ )
  {
      delete (&it);
  }

X-G

1) map<int, Tile> :P

2) No. delete (*it);

Don Freeman

I tried your delete (*it)...I got this error:

error: type ‘struct std::pair<const int, Tile*>’ argument given to ‘delete’, expected pointer

Second:
As for your adding object code...I get multiple calls to the copy constructor and then therefor multiple calls to the destructor. I don't really want that:

  Tile pTile;
  pTile.type = Terrain_Forest;
  tiles.insert ( pair<int,Tile>(0,pTile) );
  printf("size of tiles<int,Tile*>: %i\n",tiles.size());
  tiles.clear();
  printf("After CLEAR()...size of tiles<int,Tile*>: %i\n",tiles.size());

X-G
Quote:

I got this error:

Oops, sorry. I keep forgetting that map iterators aren't like the other ones. I think it's i->second() or something like that. Check the manual.

Quote:

I don't really want that:

There's no way around it. Write your constructors and destructors to cope with it.

(Also, maps overload the [] operator: tiles[0] = Tile(...);)

Don Freeman

Well, for just one add operation...I get 5 calls to the destructor on exit! That seems like crap! I might just end up using a list instead. :'(

X-G

Lists aren't all that much better. You're still going to have to write proper ctors/dtors... that goes for all STL types.

Don Freeman

Yeah....noticed that with the list class as well... Damn it...I don't like all the extra calls to the copy constructor and the extra calls to the destructor....such is life I guess....Thanks for the help X-G! ::)

Kibiz0r

Why were smart pointers not mentioned, this whole time?

You want a pointer that will call the destructor of the object that it points to as soon as it goes out of scope, yeah?

y halo thar, Smart Pointers. (part of Boost)
http://www.boost.org/libs/smart_ptr/smart_ptr.htm

Edit: Oh, okay, nevermind, cookies were already given. Then I retract my help, seeing as it's not wanted. ;)

Carrus85

Kibiz0r: I'm guessing because smart_ptr isn't part of the standard library (auto_ptr is, but it doesn't like being placed into a container).

Kibiz0r

Well, you can write your own template easy enough.

gillius

I second the smart pointer solution. Spending so much time to avoid using a tool that works...

X-G

I figure it was a good opportunity for him to learn how these things work.

Thomas Fjellstrom
Quote:

I figure it was a good opportunity for him to learn how these things work.

I for one agree with you.

ImLeftFooted
Quote:

Also, maps overload the [] operator:

tiles[0] = Tile(...);

Insert is a bit faster I think (depending on the speed of your default ctor).

tiles.insert ( pair<int,Tile>(0,pTile) );

Its a bit easier / cleaner to use make_pair

tiles.insert ( make_pair(0,pTile) );

Quote:

auto_ptr ... doesn't like being placed into a container.

Hm? I was always under the impression that it would go into a container just fine.

X-G

Yeah, if you're spending time trying to make your stuff faster by switching [x] for table.insert, you really suck at optimizing. :P

Kibiz0r
Quote:

Hm? I was always under the impression that it would go into a container just fine.

The copy constructor transfers ownership of the pointer'd object, which will break a lot of container functions.

ImLeftFooted
Quote:

The copy constructor transfers ownership of the pointer'd object, which will break a lot of container functions.

Which functions break?

Kibiz0r

Perhaps "a lot" was too brash. :)

Really, just some of the <algorithm> functions.

Only ones I'm sure of are the copy() functions, though I suspect fill() uses a copy constructor.

According to http://www.awprofessional.com/articles/article.asp?p=30642&seqNum=9&rl=1, a container of auto_ptrs ought to give you a compiler error. I wouldn't know, I pretty much only use boost::shared_ptr.

ImLeftFooted
Quote:

Only ones I'm sure of are the copy() functions, though I suspect fill() uses a copy constructor.

Pretty much every container type uses the copy constructor. What makes them "fail" is that the 'copied from' elements go to NULL. Which isn't really failing, it just means you have to know what you're doing.

The Linked Article said:

In fact, the standard defines auto_ptr in such a way that it's illegal to instantiate an STL container with an auto_ptr element type; such usage should produce a compile-time error (and probably a cryptic one, at that). However, many current implementations lag behind the standard.

This is the interesting point here. Lets assume 'element type' means the type in vector<type>.

Okay, I did some research. Apparently his not the only guy claiming this. Heres an article on auto_ptr.

The Article said:

So the standards committee bent over backwards to do everything it could to help you out: The Standard auto_ptr was deliberately and specifically designed to break if you try to use it with the standard containers (or, at least, to break with most natural implementations of the standard library). To do this, the committee used a trick: auto_ptr's copy constructor and copy assignment operator take references to non-const to the right-hand-side object. The standard containers' single-element insert() functions take a reference to const, and hence won't work with auto_ptrs.

So looks like the standards committee doesn't like auto_ptr in container types. Thats a shame. I have to say I disagree with them.

Thread #591027. Printed from Allegro.cc