Archived

This topic is now archived and is closed to further replies.

Tyson

proper way to de-alloc list<type*> ?

Recommended Posts

I was just wondering what the accepted "correct" way was to clear an STL list of pointer types. What way will do it correctly without invalidating any iterator you may be using during the clearing process? eg:
  
list<string*> myList;

myList.push_back( new string("test") );

list<string*>::iterator it;
for(it=myList.begin(); it != myList.end();it++)
{
  delete *it;
}

myList.clear();

  
is that a correct way to do it? Thanks! ============================ A guy, some jolt, and a whole lot of code

Share this post


Link to post
Share on other sites
Yes, that's the way. It doesn't invalidate iterators, in fact it doesn't even alter the data stored in the container (the pointers). It simply deletes the data where the elements point at.

Someone is bound to nag you about this though:

"it++"

So I'll just have to nag first . When using other than primitive types,

"++it"

is faster. myList.end() requires a copy so it isn't superfast either. Here's a fast version:


    
list<string*>::iterator it = myList.begin();
list<string*>::iterator eit = myList.end();
for(; it != eit; ++it)
{
delete *it;
}


[edited by - civguy on May 28, 2003 5:26:43 PM]

Share this post


Link to post
Share on other sites
That''s how I clear a container of pointers; loop through all elements and free the allocated memory, and then clear the entire container. delete *it will not affect the iterator, so it shouldn''t invalidate any iterators.

But I have some things to say about your for loop. end() will, unless the compiler is able to see that the container isn''t changed, be evaluated every iteraton. i++ should be ++i. May not matter much now when the datatype is a simple pointer, but for complex objects you should definitely use pre-in/decrements as they don''t have to mess with copies of objects. So get used to pre-in/decrements.

Share this post


Link to post
Share on other sites
It's worth mentioning why the prefix operator is "faster". The general convention is for the prefix operator to simply increment the value and return a reference to it, where as postfix operators store a copy of the value, increment the original, and return the copy. If the "create a copy" operation is costly, or is done a large number of times, it will run slower.

- Jason Citron
- ZeroInfinity Studios
- www.zeroinfinity.net

[edited by - clash on May 28, 2003 5:34:17 PM]

Share this post


Link to post
Share on other sites
Thanks for the great replies! I was aware that ++i is faster than i++ but I never even thought about that. The end() call is a good thing too. Thanks everyone

Share this post


Link to post
Share on other sites
If you are storing pointers in a STL container you could put them in an smart pointer (don''t use auto_ptr though), then you don''t have to care about deleting them, the destructor will take care of it for you.

Instead of:

  
std::list<string*> myList;
myList.push_back(new string("test"));
...
for (std::list<string*>::iterator it = myList.begin(); it != myList.end(); ++it)
{
delete *it;
}
myList.clear();

You would have:

  
typedef boost::shared_ptr<string> SharedStringPtr;
std::list<SharedStringPtr> myList;
myList.push_back(SharedStringPtr(new string("test")));
...
myList.clear();


[How To Ask Questions|STL Programmer''s Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]

Share this post


Link to post
Share on other sites
dalleboy, if the container is made to own the pointers, why be so wasteful? I changed some of my older code to use smart pointers (boost''s shared_ptr) and it became some 20% slower and I even had to write more code than the raw pointer solution used. For resources that have a clear owner (such as a single STL container), I don''t suggest using smart pointers.

Share this post


Link to post
Share on other sites
quote:
Original post by civguy
dalleboy, if the container is made to own the pointers, why be so wasteful?

Because it is simpler plus you won''t get any memory leaks. The boost::shared_ptr was just a suggestion, you could write your own smart pointer if you want to and are into optimization.

[How To Ask Questions|STL Programmer''s Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]

Share this post


Link to post
Share on other sites
quote:
Original post by civguy
dalleboy, if the container is made to own the pointers, why be so wasteful?

quote:
Original post by dalleboy
Because it is simpler plus you won't get any memory leaks. The boost::shared_ptr was just a suggestion, you could write your own smart pointer if you want to and are into optimization.

Simpler? You have to either .get() the raw pointer out of the smart one to pass it around or then make all your functions use smart pointers. Hardly compensates for the small gain in deleting the data from the container, which can be templated to work like this:
deleteContainer(cont);  

Making your own smart pointer that is faster than boost::shared_ptr is yet less simple.

I don't know how it solves memory leaks either. If it was totally transparent, it would solve them. But now that I have to actively type
typedef boost::shared_ptr SharedStringPtr;
And then use SharedStringPtr everywhere, so it's actually more burden than adding a deleteContainer(cont); into the destructor.

edit: sigh, I had just recently been doing stuff where I only had to add stuff to container, not delete anything from it except clear it every once in a while, so I oversimplified this. But naturally one has to call delete each time an item is popped from the container, not just when the whole container is erased, and that adds some remembering burden that shared pointers would take care of.
quote:
The reason to use smart-pointers in STL containers is for exception safety.
If the container's elements are deleted in a destructor and along the way as items are removed from the container, it's no less exception safe than a container using smart pointers. Try/catch helps where RAII is too much of a burden.

[edited by - civguy on May 29, 2003 3:01:29 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by civguy
I don''t know how it solves memory leaks either. If it was totally transparent, it would solve them. But now that I have to actively type
typedef boost::shared_ptr<string> SharedStringPtr;
And then use SharedStringPtr everywhere, so it''s actually more burden than adding a deleteContainer(cont); into the destructor.

As I said: "you could write your own smart pointer if you want to". This smart pointer could have an implicit constructor (boost::shared_ptr''s constructor is explicit) and an implicit convertion operator to the contained type (boost::shared_ptr doesn''t have an implicit convertion operator, you must go via the get member function), then it will be totally transparent.

[How To Ask Questions|STL Programmer''s Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]

Share this post


Link to post
Share on other sites
for neatness try:

  
struct delete_object
{
template <typename T>
void operator()(T* p){ delete p;}
};

for_each( myList.begin(), myList.end(), delete_object());
myList.clear();


Share this post


Link to post
Share on other sites