views:

172

answers:

6

Hello, how can i free up memory in a pointer vector? Here's the code:

class A
{
    private:
        int x,y,z;
    public:
        A(param1, param2, param3)
        {
            x=param1;
            y=param2;
            z=param3;
        }
        ~A()
        {
            //prompts an alertbox, warning me about the successful call of the destructor;
        }
};

...
vector<A*> list;
list.push_back(new A(1,2,3));

list.erase(list.begin()+index);//SHOULD delete the object from the memory;
list.clear();

I found out that .erase() doesn't free up memory, neither calls the destructor; i tried to use delete on every list entry with an iteration, but crashes after one iteration. Already checked if the list entry was alredy NULL, to avoid any error. Am I missing something? Also, i must use only STL, don't need Boost.

A: 
for(size_t i = 0; i < list.size(); ++i)
{
    delete list[i];
}

list.clear();

If your make something like this and your code crashes, post exact code and crash information.

Alex Farber
Using iterators here will be more efficient and container independent (i.e. u can later switch to list w/o random access support).
Vlad Lazarenko
+7  A: 

list.erase will deallocate the memory for its member elements (and call their destructors, if they exist); it will not call delete on them.

A Boost shared_ptr would be the obvious way of doing this. If you don't want to use that, you're either going to write your own smart-pointer class, or iterate through list and call delete on each pointer before calling erase. You can do this neatly with something like:

void my_delete(A *p)
{
    delete p;
}

...

std::for_each(list.begin(), list.end(), my_delete);
Oli Charlesworth
+1: Always, always, use smart pointers unless you really know what you're doing.
DeadMG
@DeadMG, yeah tell me more about cyclic dependencies with shared_ptr :-)
Vlad Lazarenko
@Oli, shared_ptr is definitely not the obvious way as there is no intent to actually share these objects. std::unique_ptr from C++0x, boost's ptr_vector or in the worse case - intrusive ptr seems to be more obvious choices.
Vlad Lazarenko
@Vlad Lazarenko: Learn to use weak_ptr, that's what it's for :P. Seriously, you can mix shared and raw pointers. I'm not trying to claim that shared_ptr is God or that it's right for every situation, but it makes a pretty good default and if you can't write simple functors for std::for_each, you certainly won't be dealing with cyclic dependencies anytime soon.
DeadMG
@DeadMG, do you really think that struggling with shared_ptr and weak_ptr is easier than just having a vector of dynamically allocated objects?
Vlad Lazarenko
@Vlad: Yes, because a vector of dynamically allocated objects is not exception safe, and results in leaks whenever you try to use a remove-like algorithm on that container. More importantly, DeadMG didn't say `shared_ptr`, he said a smart pointer of any description. `Unique_ptr` is probably the best solution, but it is not possible until C++ 0x, so we really cannot consider it here.
Billy ONeal
@Billy, it depends on how you use it. You might not throw exceptions at all and/or wrap around insertion to delete object if op fails. Really depends on what you are trying to achieve.
Vlad Lazarenko
@Vlad: Do you use the STL? If so, then pretty much every insertion or removal from a container may throw exceptions. (Specifically std::bad_alloc)
Billy ONeal
@Billy, that is true. But what is more efficient, use shared_ptr without a good reason or wrap insertion with try/catch? Assuming I don't mind writing 2 more lines of code and really know what I am doing (well, I would use ptr_vector or unique_ptr though).
Vlad Lazarenko
@Vlad: Efficiency doesn't matter unless you are in the 20% of your program that's taking 80% of the time. Using a much less maintainable scenario (a container of dumb pointers) is not desirable. And if you are in that 20% of your program, you **still** shouldn't be using a container of dumb pointers, you should be using something like `ptr_vector` which can clean up after itself (which isn't hard to implement oneself). And `unique_ptr` isn't possible at the moment, so I really don't see why people keep bringing it up. Sure it's nice, but it's not even possible to implement in the current std.
Billy ONeal
@Vlad: Also note, DeadMG's comment says SMART pointer, not SHARED pointer. There's a difference. `Unique_ptr` and `auto_ptr` are perfectly valid smart pointers.
Billy ONeal
@Billy, I stated shared_ptr because Oil said it is obvious solution, but it is not. std::unique_ptr is widely used by top-notch firms, it is already implemented (not possible? :-)) in gcc's STL 4.5 and above, I believe in MS compiler and Intel's as well. You are totally missing the point with efficiency though. I am not talking about general cases here, GUI applications and so force. I am talking exactly about the cases where it matters and you trying to shave microseconds out of your program.
Vlad Lazarenko
@Vlad: I say not possible `unique_ptr` requires move semantics to implement. Move semantics are not in the current standard. Therefore it is not possible to implement in the current standard.
Billy ONeal
@Billy: who said in the current standard ? The OP asked not to use `boost` but didn't say anything about its compiler version or the STL she used.
Matthieu M.
+3  A: 
for( std::vector<A*>::iterator i = list.begin(), endI = list.end(); i != endI; ++i)
{
   delete *i;
}
list.clear();

or, using new lambda functions

std::for_each( list.begin(), list.end(), []( A* element) { delete element; });
list.clear();
Cătălin Pitiș
+1 for answering the question The Way It Was Meant To Be Answered.
rubenvb
I wish it was working...it gives me the same errors as before.
Tibor
I don't know where it can crash, but I am almost certain that it is not in the for_each loop. Maybe you are accessing some of the deleted objects after destruction, which will definetly provoke a crash.
Cătălin Pitiș
+3  A: 

erase only erases what's in the vector (the pointers) without doing anything about what they might point at.

If you want what the point at deleted, you need to handle that yourself.

My advice would be to avoid handling any of this yourself, and consider using Boost ptr_vector instead.

Jerry Coffin
Even though the OP asked not to use boost, I still prefer this answer. Programming in C++ without Boost is like climbing with an arm tied in the back: possible, but stupid.
Matthieu M.
+1  A: 

Upon destruction, an STL container will destroy the objects it contains. If those objects are pointers, then it will destroy the pointers. For naked, dumb pointers, this will not delete the objects they point to. That's why it is usually best to use smart pointers for that. Smart pointers will delete the objects they refer to upon deletion; std::shared_ptr keeps track of copying pointers and how many references to a given object exist, and will only delete the object when the last pointer dies. This is always a good first candidate when looking for a suiting smart pointer. Your container would then be declared like this: std::vector< std::shared_ptr<A> >

However, your compiler/std lib might not come with std::shared_ptr, which is a feature of the next C++ standard, generally expected next year. It might, however, come with std::tr1::shared_ptr, which is a TR1 feature from 2003. (If all else fails, boost has boost_shared_ptr, but you already ruled out boost.)

You can manually manage dynamically allocated objects in STL containers, but it's a burden and prone to errors. For example, you must prevent functions from returning early (before the manual cleanup) through return statements or exceptions, and you must watch out for copy operations on containers. (Otherwise two containers would have pointers referring to the same objects, which you might then try to destroy twice.)
Manually managing resources is a PITA, prone to errors, and best avoided.

sbi
+1  A: 

The code you have posted is not legitimate C++. Plus, erase does not delete objects you have allocated, it only erases contents of the vector, which in your case are pointers. The actual objects that you have allocated are not being deleted. Here is a correct way of doing what you want:

#include <vector>
#include <algorithm>

class A
{
    int x,y,z;

public:
    A (int param1, int param2, int param3) :
        x (param1), y (param2), z (param3)
    {
    }
};

struct Deleter
{
    template <typename T>
    void operator () (T *obj) const
    {
        delete obj;
    }
};

int
main ()
{
    std::vector<A*> list;

    list.push_back (new A (1, 2, 3));
    list.push_back (new A (4, 5, 6));
    list.push_back (new A (7, 8, 9));

    std::for_each (list.begin (), list.end (), Deleter ());
    list.clear ();
}

You may also look at Boost Ptr Container library that solves this problem in a safe and reusable manner. In C++0x, there is a std::unique_ptr template class that supports movable semantics and can be used with STL containers and algorithms to clean up memory automatically.

Vlad Lazarenko