views:

247

answers:

11

I have an abstract Base class and Derived class.

int main ()
{
  Base *arrayPtr[3];

  for (int i = 0; i < 3; i++)
  {
    arrayPtr[i] = new Derived();
  }

  //some fuctions here

  delete[] arrayPtr;

  return 0;
}

I'm not sure how to use the delete operator. If I delete array of base class pointers as shown above, will this call derived class objects destructors and clean the memory?

+10  A: 

No, you have to explicitly delete each item in the array:

for (int i = 0; i < 3; ++i)
{
    delete arrayPtr[i];
}
fretje
@Roger I see no delete[]
James Hopkin
@Roger: realized that after my first post... was already edited out, but thanks anyway ;)
fretje
James: It was edited out within the first 5 minutes.
Roger Pate
+10  A: 

You have to iterate over the elements of your array, delete each of them. Then call delete [] on the array if it has been allocated dynamically using new[].

In your sample code, the array is allocated on the stack so you must not call delete [] on it.

Also make sure your Base class has a virtual destructor.

Reference: When should my destructor be virtual.

Gregory Pakosz
+1 for `virtual` destructor point
Red Hyena
No need to call delete[] on the array, is it has not been 'newed'.
fretje
yes I was editing my answer when you wrote your comment, thx
Gregory Pakosz
I considered iterating over the array, but I didn't know about virtual destructor.Thanks.
Mike55
Base *arrayPtr[3]; wasn't newed, and deleting it with delete[] wouldn't do the right thing!
Dmitry
+1  A: 

You should instead do:

 for ( int = i; i < 3; i++ )
 {
    delete arrayPtr[i];
 }

And you shouldn't do delete[] arrayPtr; as you're trying to free/delete a stack allocated arrayPtr.

Another thing to consider is using a std::vector of pointers instead of an array. And if you're using a compiler that implements TR1, you could also use a std::vector of std::tr1::shared_ptr instead of raw pointers, and than you wouldn't need to worry about deleting those objects yourself.

Example:

{
    std::vector< std::tr1::shared_ptr<Base> > objects;
    for (int i=0; i < 3; ++i)
    {
        objects.push_back(std::tr1::shared_ptr<Base>(new Derived()));
    }
}  // here, once "objects" exit scope, all of your Derived objects are nicely deleted
Dmitry
Shared_ptr<> is not the answer to everything!!! The pointer is not shared for a start. boost::ptr_vector<> may be a better choice.
Martin York
Agree, just was suggesting "the least of evils", in a sense, that I already have shared_ptr on my macbook with gcc, and to use boost::ptr_vector I'd need to drag boost in project, which might not be necessary.
Dmitry
@Martin York: `shared_ptr<>` isn't *the* answer to everything, but it's *a safe* answer to most pointer questions. I generally use it unless I've got a reason to use something different, and am reasonably comfortable suggesting it in general situations.
David Thornley
A: 

Make sure Base has a virtual destructor. Then like fretje outlined, delete each element in the array, then delete the array.

You should be using std::vector for the array. That said, you should really be using a container made for this sort of thing. (So you don't accidentally fail to delete all the elements, which will definitely be the case if an exception gets thrown!) Boost has such a library.

GMan
Although "you should use containers" is a definitely a top advice, somehow it shouldn't be thrown in too quickly. I mean 1) understand what's going on by doing it by hand, then 2) in production code leverage rock solid libraries like the STL and Boost. Otherwise, isn't there a tendency to consider the containers and other tools provided by libraries as only black boxes? What do you think?
Gregory Pakosz
Gregory: Disagreed. At some point you should understand what's going on under the hood (maybe), but it's no longer clear to me in what order that should be (I used to agree with you). Should you understand how malloc works before you can call new? Should you understand how vtables work before using dynamic_cast (and *then* find out that isn't a guaranteed part of the implementation)? Both no. That said, you should always know where to go to look up the information you need, or who to talk to, etc.
Roger Pate
@Roger I get your point. Taking your examples, you don't need to know the implementation details of heap memory allocations still it's good to know the difference between the heap and the stack. You don't need to know how vtable is implemented and such, still you have to know calling a virtual method makes an indirection. And finally algorithms and data structures are gold: you definitely have to know the difference between a contiguous array and a list concepts even though you're using std::vector or std::list implementations; otherwise in the long run you just lack clues :)
Gregory Pakosz
+1  A: 

You have to delete the members of the array individually. You must also make sure that your base class has a virtual destructor. You might also want to consider making it an array (or better still a std::vector) of smart pointers, such as boost::shared_ptr.

anon
+1  A: 

No, you can not do that. As others suggested you have to go through each item and delete it. It's a very simple rule to remember. If you allocated using new then use delete, and if you had used new[] then use delete[]

Naveen
A: 

No, this doesn't quite do what you want.

There are two points to watch out for here:

  1. The syntax delete[] arrayPtr is used if you dynamically allocate the array, like this:

    arrayPtr = new (Base *)[mylength];

    In your case, however, you have a statically allocated array, so there is no need to delete it. You do, however, need to delete the individual elements in the array:

    for ( int = i; i < 3; i++ ) delete arrayPtr[i];

  2. The second point you need to take care of is to make the destructor of the class Base virtual:

    class Base { virtual ~Base(); /* ... */ };

    This ensures that when you call delete on a Base * that is actually pointing to a Derived, the destructor of Derived is called, not just the destructor of Base.

Martin B
+1  A: 

Notice what's absent:

int main() {
  boost::ptr_vector<Base> v;
  for (int i = 0; i < 3; i++) v.push_back(new Derived());
  // some functions here, using v[0] through v[2]
}

Check Boost's pointer containers out.

Roger Pate
+1  A: 

Operator delete must match operator new on that pointer, if it was allocated with new[], you must call delete[] and vice versa;

int* pInt = new int;
delete pInt; OK
delete [] pInt; WRONG

int[] pIntArr = new int[3];
delete [] pIntArr; OK
delete pIntArr; WRONG

In your case there is something else wrong - you are trying to delete that was allocated on the stack. That wouldn't work.

You must delete each pointer individually in this particular case.

Igor Zevaka
+1  A: 

What you have there is undefined behaviour -- a bug. Each call to new needs to be matched with a delete; each call to new[] needs to be matched with a delete[]. The two are separate and can't be mixed.

In the code you posted, you have an array of pointers to Base allocated on the stack. You're then calling delete[] on an array allocated on the stack -- you can't do that. You can only delete[] an array allocated on the heap with new[].

You need a call to delete for each element allocated with new -- or preferably, look into using a container class, such as std::vector, instead of using an array.

Stephen Veiss
A: 

You're mixing paradigms -- The array delete operator is designed to free memory allocated by the array new operator, but you're allocating your array on the stack as an array of pointers and then allocating an object for each array member. In your code, you need to iterate through the array.

To use the array new operator, you'd declare like this:

Base *array;
array = new Base[3];
/* do stuff */
delete[] array;

This allocates a contiguous memory area for the three objects -- note that you've got an array of Base objects, not an array of pointers to Base objects.

Andrew Aylett