Virtual Destructors and array of objects

A

amparikh

Ok, my question is not about Virtual destructors and why, but more on
the significance.
Generally we have a virtual destructor in the base class ( and
inadvertently in the derived class) so that you can delete a
derived-class object via a base-class pointer...So, the correct
destructor(s) gets invoked(the derived class one in particular) and the
correct amount of memory is also released.

But if the above is true, why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?

I am going through the FAQ about placement new and how the whole thing
is implemented so as to keep track of the number of objects and
therefore call each corresponding destructor.

So if deleting derived objects via base pointer works for one object,
then I dont know why it wouldnt work for deleting an array of derived
objects via base pointer.

Maybe I am missing something or maybe my understanding is not
complete...can anyone throw more light on this.

Thanks.
 
P

Peter Koch Larsen

Ok, my question is not about Virtual destructors and why, but more on
the significance.
Generally we have a virtual destructor in the base class ( and
inadvertently in the derived class) so that you can delete a
derived-class object via a base-class pointer...So, the correct
destructor(s) gets invoked(the derived class one in particular) and the
correct amount of memory is also released.

But if the above is true, why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?

I am going through the FAQ about placement new and how the whole thing
is implemented so as to keep track of the number of objects and
therefore call each corresponding destructor.

So if deleting derived objects via base pointer works for one object,
then I dont know why it wouldnt work for deleting an array of derived
objects via base pointer.

Because the standard says so.
Maybe I am missing something or maybe my understanding is not
complete...can anyone throw more light on this.

Thanks.

See the thread "virtual destructors" in this group. I have given an
explanation as to why it does not work there.

/Peter
 
V

Victor Bazarov

Ok, my question is not about Virtual destructors and why, but more on
the significance.
Generally we have a virtual destructor in the base class ( and
inadvertently in the derived class) so that you can delete a
derived-class object via a base-class pointer...So, the correct
destructor(s) gets invoked(the derived class one in particular) and the
correct amount of memory is also released.
Right.

But if the above is true, why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?

I don't seem to understand the question. An array of derived objects
via base class pointer? Could you illustrate it with some code? Do you
mean

base *array = new derived[10];

? That won't work as soon as you try indexing.
I am going through the FAQ about placement new and how the whole thing
is implemented so as to keep track of the number of objects and
therefore call each corresponding destructor.

So if deleting derived objects via base pointer works for one object,
then I dont know why it wouldnt work for deleting an array of derived
objects via base pointer.

Deleting an array involves the _size_. The size is obtained from the
type of the array element...
Maybe I am missing something or maybe my understanding is not
complete...can anyone throw more light on this.

I guess you need to realize the difference between the 'delete' and
'delete[]'. If you need help, just ask (after you've searched all the
usual places for the explanations).

V
 
B

Bob Hairgrove

On 7 Jun 2005 13:41:37 -0700, (e-mail address removed) wrote:

[snip]
why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?

You can allocate an array of base class pointers and use them
polymorphically (once you hae allocated their respective objects and
assigned their respective pointers to the array elements, that is).
But delete[] will only delete the storage for the pointers themselves,
not what they point to!

As others have pointed out (and this is, or should be, a FAQ) one must
never allocate an array of *objects* polymorphically because each
array element must have the same size.
 
A

amparikh

But if the above is true, why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?
I

don't seem to understand the question. An array of derived objects
via base class pointer? Could you illustrate it with some code? Do
you
mean

base *array = new derived[10];
However when the smart ptr goes out of existence it would just do a
delete[] ptr.


? That won't work as soon as you try indexing.
I am going through the FAQ about placement new and how the whole thing
is implemented so as to keep track of the number of objects and
therefore call each corresponding destructor.
So if deleting derived objects via base pointer works for one object,
then I dont know why it wouldnt work for deleting an array of derived
objects via base pointer.


Deleting an array involves the _size_. The size is obtained from the
type of the array element...

so lets say
base* ptr = new derived;
delete ptr; //here it does the right thing if the destructor is
declared virtual....it calls the right destrcutor and frees the right
amount of memory.

now when you do
delete[] ptr; //from what I understand, it has to get the ACTUAL size
of the underlying object.

Wouldnt this be analogous to say something like a dynamic_cast
operation from a base class pointer to a derived class pointer. Even in
this case, you need to check the underlying pointer( which is what
operator delete or dynamic_cast does) and just interploate it over n
number of objects( as has been demonstarted in the FAQ via placement
http://www.parashift.com/c++-faq-lite/compiler-dependencies.html#faq-38.8)
Maybe I am missing something or maybe my understanding is not
complete...can anyone throw more light on this.


I guess you need to realize the difference between the 'delete' and
'delete[]'. If you need help, just ask (after you've searched all the
usual places for the explanations).

V
 
A

amparikh

I am actually allocating all objects of the same size, the derived
class. They arent of different sizes(or different types of derived
classes)
 
H

Howard

The rules are simply different for arrays. A base class pointer can be used
polymorphically. But a pointer to an array of objects cannot.

If you want polymorphic behavior when using an array, then make it an array
of base class pointers, and allocate (and later delete) each pointer in the
array individually. (Even better, use a vector of base class pointers!)

There are plenty of explanations about this behavior in this newsgroup and
the moderated one. Try a google search for something like "delete array
derived class objects".

-Howard
 
P

Peter Julian

Ok, my question is not about Virtual destructors and why, but more on
the significance.
Generally we have a virtual destructor in the base class ( and
inadvertently in the derived class) so that you can delete a
derived-class object via a base-class pointer...So, the correct
destructor(s) gets invoked(the derived class one in particular) and the
correct amount of memory is also released.

But if the above is true, why isnt it a good practice to allocate an
array of derived objects via base class pointer and then delete them
via this base class pointer using delete[] ptr ?

Because the array is created at compile-time. You need a container that
supports run-time compilation. In effect, the goal is not to delete the
container, but rather, you want to delete the objects within (or in your
case, the objects pointed to). Its the objects, not the container of
pointers, that are polymorphic.
I am going through the FAQ about placement new and how the whole thing
is implemented so as to keep track of the number of objects and
therefore call each corresponding destructor.

So if deleting derived objects via base pointer works for one object,
then I dont know why it wouldnt work for deleting an array of derived
objects via base pointer.

There is a huge difference since a base pointer to an object is potentially
a derivative. An array knows no derivatives (its a compile time creature).
Thats why vectors, deques, stacks, list, map, sets where created. So that
the object, not the container, has invocable and callable behaviours.
Maybe I am missing something or maybe my understanding is not
complete...can anyone throw more light on this.

You are on the right track.
 
P

Peter Julian

I am actually allocating all objects of the same size, the derived
class. They arent of different sizes(or different types of derived
classes)

No you are not, you are allocating objects but storing pointers. the array
is not an array of objects. If you delete the array, you only delete the
object's pointers. In fact, the same goes for any type of container:

#include <iostream>
#include <vector>

class Base
{
int m_b;
public:
Base(int n) : m_b(n) { std::cout << "Base ctor\n"; }
virtual ~Base() { std::cout << "\nBase d~tor"; }
};

class Derived : public Base
{
public:
Derived(int n) : Base(n) { std::cout << "Derived ctor\n"; }
~Derived() { std::cout << "\nDerived d~tor"; }
};


int main(int argc, char* argv[])
{
std::vector< Base* > v_ptr_bases;

for (int i = 0; i < 5; ++i)
{
v_ptr_bases.push_back(new Derived(i));
}

// v_ptr_bases.clear(); // no way, those are pointers

typedef std::vector< Base* >::iterator VIter;
for (VIter it = v_ptr_bases.begin(); it != v_ptr_bases.end(); ++it)
{
delete *it;
}

return 0;
}

/*
Base ctor
Derived ctor
Base ctor
Derived ctor
Base ctor
Derived ctor
Base ctor
Derived ctor
Base ctor
Derived ctor

Derived d~tor
Base d~tor
Derived d~tor
Base d~tor
Derived d~tor
Base d~tor
Derived d~tor
Base d~tor
Derived d~tor
Base d~tor
*/

There is no doubt that an auto pointer saves the day since manual
deallocation is no longer needed. But its still relevant to acknowledge that
a container of pointers is not a container of objects.
 
A

Alan Johnson

Peter said:
There is no doubt that an auto pointer saves the day since manual
deallocation is no longer needed. But its still relevant to acknowledge that
a container of pointers is not a container of objects.

I'm not sure if you were suggesting this or not, but it is worth noting
that std::auto_ptr does NOT meet the requirements to be stored in any of
the standard containers.
 
M

Me

The important thing to know is that C++ is a value based language, it
doesn't behave like other languages where you can do this (all their
objects are really pointers to objects, they just hide that from you):

struct A { int a; };

struct B : A { float f; };

vector<A> v;
B b;

//vector<A>::push_back(const A &);
v.push_back(b);

^-- What happens here is called slicing. In the call to push_back, b
gets downcasted to an A object and just copies the A object of b over
to the end of the vector. Slicing is considered to be a bad thing in
all but a few cases as you lose certain information. Agree with me so
far?

A a;
//A::eek:perator=(const A &);
a = b;

^-- Slicing also happens with regular objects because that's what the
regular assignment operator is expected to look like.

A arr[10];
//arr's index operator returns an A &, this calls the assignment
operator above
arr[2] = b;

A *narr = new A[10];
//ditto
narr[2] = b;

^-- Slicing also happens when using plain arrays of regular objects. So
you can see there is no way to store a full B into an array of A,
therefore an array of type A can only hold objects of type A (or a
const/volatile compatible A).

The standard doesn't want to bend over backwards to handle the rare
case of where you write a correct assignment operator that somehow
handles slicing because doing that requires the base and derived class
to be the same size (rare in general and not guaranteed) and in your
case, involves a memcpy to copy the vtable over (which is undefined and
assumes your implementation even uses vtables).

Not all is lost, this code is correct, see if you can figure out why:

vector<A*> v2();

// this is ok
v2.push_back(new B);
delete v2[0]; // call's B's destructor

// this is ok
A **arr2 = new A*[1];
arr2[0] = new B;
delete arr2[0] // calls B's destructor
delete [] arr2;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top