STL list question [SOLVED]

Started by
9 comments, last by CrazyCdn 16 years, 11 months ago
if I were to create a list and populate it like so


Magnus_SubMain* NewSubMain = NULL;

for(DWORD d = 0; d < m_dwNumProcessors; d++)
{
	NewSubMain = new Magnus_SubMain;

	MagnusSubMain_List.push_back(NewSubMain);
}




would a simple list.clear() function delete the memory I allocated or would I have to do this?:


std::list<Magnus_SubMain*>::iterator SM_it = MagnusSubMain_List.begin();

while(SM_it != MagnusSubMain_List.end())
{
	delete (*SM_it);
	SM_it++;
}



[Edited by - etsuja on May 25, 2007 8:25:57 PM]
Artist 1st - Programmer 2nd(I'll get some material linked here sometime to support these claims, haha)
Advertisement
The list (just like any other well-written container class) cleans up after itself. Nothing more, nothing less.
In other words, any memory allocated by the list (using new) will be freed again (using delete)

Anything you do is your responsibility.

In your example, all you give the list is a pointer. How should it know whether that pointer was new'ed? It might point to something on the stack, and so it shouldn't be deleted.

And more importantly, it might point to something you don't actually want destroyed yet! You might get rid of the list, yes, but still want to use the elements it contained. Hard to do that if the list automatically deletes everything it touches.

So as always, the rule is that you delete the memory you allocate. And you assume that any third-party code (including the standard library) does the same.

Edit: Of course, in your example, there's no need to use new at all. You could just populate the list like this:

for(DWORD d = 0; d < m_dwNumProcessors; d++) {  MagnusSubMain_List.push_back(Magnus_SubMain()); // <- no calls to 'new' at all}

And then, since you haven't actually new'ed anything, you don't need to delete anything when the list is destroyed either.

[Edited by - Spoonbender on May 25, 2007 8:35:48 PM]
You have to delete the memory yourself. You new it, you delete it. Least with the SC++L.

And you can save a line of code:
	NewSubMain = new Magnus_SubMain;	MagnusSubMain_List.push_back(NewSubMain);


to

	MagnusSubMain_List.push_back(new Magnus_SubMain);

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

These two posts have me confused...

so if I use this way the library will delete it for me?

MagnusSubMain_List.push_back(new Magnus_SubMain);


and this way it wont?

NewSubMain = new Magnus_SubMain;

MagnusSubMain_List.push_back(NewSubMain);
Artist 1st - Programmer 2nd(I'll get some material linked here sometime to support these claims, haha)
Those are basically the same you allocated them so you have to delete them by looping and calling delete on each. What you need to look out for is that those pointers are not passed around your app and used at some point after you have deleted them.
You will need to clean up after yourself in both cases (since you call new in both cases).
Quote:Original post by etsuja
These two posts have me confused...

so if I use this way the library will delete it for me?

MagnusSubMain_List.push_back(new Magnus_SubMain);


and this way it wont?

NewSubMain = new Magnus_SubMain;

MagnusSubMain_List.push_back(NewSubMain);


Every time you have a 'new' in your code, you need to call 'delete'.
Note that in my example, there was no 'new' at all. The variable is created on the stack, and stored in the list (which then has to have type std::list<Magnus_SubMain>, without the *)

When the list is destroyed, it calls the destructor on the elements it contains. In your code, it only contains pointers, and they don't have a destructor. All that happens to them is that they.. go away. It doesn't touch what the pointers point to. (Because it can't know whether the objects they point to should be deleted, or even if they are valid)
But if you store the object itself in the list, the object's destructor will be called when the list is destroyed. And then your objects will be destroyed for you.
Righteous, thanks for the info guys. I guess I should have rememberd when I first learned about new which I do now that it sad for every new there must be a delete.

@Spoonbender: I think I will do it that way since the elements don't have to be in dynamic memory.

Artist 1st - Programmer 2nd(I'll get some material linked here sometime to support these claims, haha)
If you are keeping pointers to things in an STL container and you know that the container will always 'own' the pointers you can implement a templated deletion functor that, in combination with for_each, will delete everything safely. This could be used in a 'clear resources' function, or a containing object's destructor if you know that the resources' lifetimes shouldn't outlast the container.

template <typename T>struct PtrCollectionDelete{  void operator()(T * target) { delete target; target = NULL; }};// Assuming you have e.g. a std::vector<Resource*> resources then to delete them all you can usestd::for_each(resources.begin(),              resources.end(),              PtrCollectionDelete<Resource>());


(Haven't checked this code btw, but should be OK I think.)

L
-

Visit http://www.mugsgames.com

Stroids, a retro style mini-game for Windows PC. http://barryskellern.itch.io/stroids

Mugs Games on Twitter: [twitter]MugsGames[/twitter] and Facebook: www.facebook.com/mugsgames

Me on Twitter [twitter]BarrySkellern[/twitter]

Quote:Original post by PKLoki
If you are keeping pointers to things in an STL container and you know that the container will always 'own' the pointers you can implement a templated deletion functor that, in combination with for_each, will delete everything safely. This could be used in a 'clear resources' function, or a containing object's destructor if you know that the resources' lifetimes shouldn't outlast the container.

*** Source Snippet Removed ***

(Haven't checked this code btw, but should be OK I think.)

L
-


You are setting a copy of the pointer to NULL, which is ineffectual. You would need to pass the pointer by reference in order to modify the value in the container. The standard is fuzzy on the legality of doing that, though, so you would probably end up using transform instead.

That said, your function object can be improved:

struct ptr_delete {  template< class T >  void operator()( T * const p ) const {    delete p;  }};


Which will take advantage of automatic template deduction:

std::for_each( resources.begin(), resources.end(), ptr_delete() );
resources.clear()


Now, a version that would use transform() would look like this:

struct ptr_delete {  template< class T >  T * operator()( T * const p ) const {    delete p;    return 0;  }};// ...std::transform( resources.begin(), resources.end(), resources.begin(), ptr_delete() );


That is, of course, assuming that you want to allow null values in your container.


All in all, one would probably be better off with the Boost Pointer Containers or a container of boost::shared_ptrs (edit: or, as Spoonbender has suggested, not use dynamic memory at all).


jfl.

This topic is closed to new replies.

Advertisement