Virtual Destructor

  • Thread starter Prawit Chaivong
  • Start date
P

Peter Julian

Prawit Chaivong said:
Hi All,

There is code here.
------------------------------------------------------------------
class Base{
public:
Base(){}
virtual ~Base(){}
private:
int a;
};

class Derived : public Base{
public:
Derived(){}
virtual ~Derived(){}
private:
int b;
};

int main()
{
Base* pb = new Derived[10];
delete[] pb; // <-- crash HERE.
return 0;
}
-------------------------------------------------------------------
From code, I know that I shouldn't delete Derived object by its parent
pointer. Because size of both are not similar.

But my question is when I remove keyword 'virtual' out of
destructor(both classes). This program is not crash anymore.
I just want to know why it's not crash if its dtor is not virtual.

Without the virtual destructor:
You are then deleting 10 times the equivalent of a new Base allocation.
Which is undefined behaviour here since the objects are not of Base type.
Can you say: memory leak? Note that pb is *not* a pointer to an array (pb
points to a single Base object). That too is undefined behaviour when you
delete [] pb.

When the destructor in Base is made virtual and an array of Base pointers is
implemented:

fact #1: an array is not responsible to track whether its elements are Base
or Derived.
fact #2: an array requires an appropriate default constructor. Otherwise the
following will fail:

Base* pb[10];

fact #3: OO programming says that each element knows what type it actually
is through polymorphism. The container doesn't care. In your case, thats a
container of Base pointers. What those pointers actually point_to is
irrelevent as far as the array is concerned.

fact #3: to delete each element in a container of base-class pointers, you
need to delete the elements, not the container. Its not the container that
was allocated on the heap, its the elements that were newed.

fact #4: A derived class should initialize its Base class in the derived
class's initialization list.

So...

#include <iostream>

class Base
{
public:
Base() : a(0) { std::cout << "Base ctor invoked.\n"; }
virtual ~Base() { std::cout << "Base d~tor invoked.\n"; }
private:
int a;
};

class Derived : public Base
{
public:
Derived() : Base(), b(0) { std::cout << "\tDerived ctor invoked.\n"; }
~Derived() { std::cout << "\tDerived d~tor invoked.\n"; }
private:
int b;
};

int main()
{
const int size = 5;

Base* barray[size]; // container is allocated on the stack

std::cout << "*** array element allocations ***\n";

for (int i = 0; i < size; ++i)
{
barray = new Derived(); // elements are allocated on the heap
}

std::cout << "\n*** array element destructions:***\n";

for (int j = 0; j < size; ++j)
{
delete barray[j] ;
}

return 0;
}

/*
*** array element allocations ***
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Derived ctor invoked.

*** array element destructions ***
Derived d~tor invoked.
Base d~tor invoked.
Derived d~tor invoked.
Base d~tor invoked.
Derived d~tor invoked.
Base d~tor invoked.
Derived d~tor invoked.
Base d~tor invoked.
Derived d~tor invoked.
Base d~tor invoked.
*/

________________________________________
to confirm that polymorphism does indeed occur lets alternate the element
type for each element in the array:

....

int main()
{
Base* barray[5];

std::cout << "*** array element allocations ***\n";

barray[0] = new Derived();
barray[1] = new Base();
barray[2] = new Derived();
barray[3] = new Base();
barray[4] = new Derived();

std::cout << "\n*** array element destructions:***\n";

for (int j = 0; j < 5; ++j)
{
delete barray[j] ;
}

return 0;
}


/* output:
"*** array element allocations ***
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Base ctor invoked.
Derived ctor invoked.
Base ctor invoked.
Base ctor invoked.
Derived ctor invoked.

"*** array element allocations *** // destructions....

Derived d~tor invoked.
Base d~tor invoked. // barray[0] ... a Derived object
Base d~tor invoked. // barray[1] ... a Base object
Derived d~tor invoked.
Base d~tor invoked. // barray[2] ... a Derived object
Base d~tor invoked. // barray[3] ... a Base object
Derived d~tor invoked.
Base d~tor invoked. // barray[4] ... a Derived object
*/
 
P

Peter Julian

Fraser Ross said:
The OP wanted polymorphism in an array which you said can be done, then you
gave an example of doing it in a vector. Although it can be done with an
array it is tedious to do the setting up of the array. The objects would
have to be created and destroyed elsewhere.

Fraser.

A vector is an array.
The OP's problem is due to the fact that he's using an array. The array is
still a compile-time creature. In the old days, it was generally accepted
that an array had the same behaviours as the type it contained. OOP has
changed that but the array's interface does not reflect it. Hence: the
vector. Problem solved.

The difference between a vector and an array is that the vector's interface
deals with its elements. At no point does the vector container's interface
suggest that the container inherits the attributes of the elements within.
Thats the OP's confusion.

He's trying to allocate an array on the stack, then allocate the elements on
the heap and then attempts to delete the array off the heap. Thats undefined
behaviour.
 
O

Old Wolf

Peter said:
Prawit Chaivong said:
class Base{
public:
Base(){}
virtual ~Base(){}
};

class Derived : public Base{
public:
Derived(){}
virtual ~Derived(){}
};

int main()
{
Base* pb = new Derived[10];
delete[] pb; // <-- crash HERE.
return 0;
}

Without the virtual destructor:
You are then deleting 10 times the equivalent of a new Base
allocation. Which is undefined behaviour here since the objects
are not of Base type.

Ok so far..
Note that pb is *not* a pointer to an array (pb points to a single
Base object).

pb points to a single Base object, which is the first member
of an array of 10. (to be precise).
That too is undefined behaviour when you delete [] pb.

Rubbish. 'delete []' is the correct syntax for deleting
an array allocated with 'new []'. Here's another example
of correct code:

int *p = new int[10];
delete [] p;
When the destructor in Base is made virtual and an array of Base
pointers is implemented:

The OP code was an array of objects, not an array of pointers.
But let's continue anyway...
fact #2: an array requires an appropriate default constructor.

Rubbish. Arrays do not have constructors at all.
Otherwise the following will fail:

Base* pb[10];

That code is correct and does not require Base to have a
default constructor either.
fact #4: A derived class should initialize its Base class in
the derived class's initialization list.

Rubbish. An initialization is only necessary if a non-default
constructor of Base is being used.
class Derived : public Base
{
public:
Derived() : Base(), b(0) { }

The "Base()," is obfuscation and should be removed.
barray[0] = new Derived();
barray[1] = new Base();
barray[2] = new Derived();
barray[3] = new Base();
barray[4] = new Derived();

These brackets are all superfluous.
 
P

Peter Julian

Old Wolf said:
Ok so far..


pb points to a single Base object, which is the first member
of an array of 10. (to be precise).

Wrong, pb points to a single Base object *but* the objects are *not* Base
type. Or rather, the objects are not neccessarily Base types. Here lies the
problem.

An array is a compile-time created container. An array is incapable of
determining the run-time type of its elements.
That too is undefined behaviour when you delete [] pb.

Rubbish. 'delete []' is the correct syntax for deleting
an array allocated with 'new []'. Here's another example
of correct code:

int *p = new int[10];
delete [] p;

Does not apply. delete can only delete the array of integer pointers. In the
OP's case, the int is not an int. Therefore the delete [] pb will fail to
deallocate correctly.
The OP code was an array of objects, not an array of pointers.
But let's continue anyway...

Wrong. The OP's array is definitely an array of Base*. Note the presence of
new:

Base* pb = new Derived[10];

not...

Base b = new Base[10];
Rubbish. Arrays do not have constructors at all.

Who said anything about an array's ctor? You can't construct an array of any
user-defined type (non-primitive object) that doesn't provide a default
constructor. Thats old news.

class foo
{
int f;
public:
foo(int n) : f(n) { }
};

int main ()
{
foo f[10]; // no appropriate default constructor available
}
 
K

Karl Heinz Buchegger

Peter said:
Wrong, pb points to a single Base object *but* the objects are *not* Base
type. Or rather, the objects are not neccessarily Base types. Here lies the
problem.

An array is a compile-time created container. An array is incapable of
determining the run-time type of its elements.

To this last section I can't agree.
An array knows exactly what objects it stores.
The problem in the OP's code is that he doesn't have an array to
begin with. He has a pointer.
That too is undefined behaviour when you delete [] pb.

Rubbish. 'delete []' is the correct syntax for deleting
an array allocated with 'new []'. Here's another example
of correct code:

int *p = new int[10];
delete [] p;

Does not apply. delete can only delete the array of integer pointers.

There is no array of integer pointers.
There is only one pointer.
In the
OP's case, the int is not an int. Therefore the delete [] pb will fail to
deallocate correctly.

Nonsense
The only problem the OP might have, and thats why he asked, is that
his destructor has to be virtual. Which is a funny thing, because the
OP tells us that in his program it is actually the opposite: When
the destructor is correctly declared as virtual, it crashes. If he
drops the destructor, then the program works (Well, I know: He has
undefined behaviour in this case and that includes, seems to work).
The later case is not very surprising, the former case is: the program
is correct and should work as expected. There must be something in his
real program that he didn't show.
The OP code was an array of objects, not an array of pointers.
But let's continue anyway...

Wrong. The OP's array is definitely an array of Base*. Note the presence of
new:

Base* pb = new Derived[10];

That is not an array of pointers. It *is* an array of objects. And there
is a single pointer pointing to it.
You might want to make sure you know what you are talking about.
not...

Base b = new Base[10];

That's a syntax error.
 
A

Alf P. Steinbach

* Karl Heinz Buchegger:
the program is correct and should work as expected. There must be
something in [the OP's] real program that he didn't show.

The program is incorrect.

See my first reply in this thread.
 
K

Karl Heinz Buchegger

Alf P. Steinbach said:
* Karl Heinz Buchegger:
the program is correct and should work as expected. There must be
something in [the OP's] real program that he didn't show.

The program is incorrect.

Aeh. right.
I just did a quick glance through the program and missed
the attempt of a polymorphic array which of course will
not work (for the reasons you pointed out).
 

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,754
Messages
2,569,527
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top