By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,889 Members | 1,297 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,889 IT Pros & Developers. It's quick & easy.

Virtual Destructor

P: n/a
Please answer my questions below - thanks!

1. Why "Derived constructor" is called but "Derived destructor" not in Case
1 since object B is new'ed from Derived class?
2. Why "Derived destructor" is called in Case 2 since only ~base() becomes
"virtual" and ~Derived() is still non-virtual?
3. Does Case 3 show that we don't need any virtual destructor to make
~Derived() called?
4. Is "virtual destructor" needed only for Case 2?
Case 1:
========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n"; }
};
int main(){
base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Base destructor

Case 2:
========

class base
{
public:
virtual ~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor

Case 3:
=========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

Derived* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor


Jul 19 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a
"Stub" <st**@asof.com> wrote...
Please answer my questions below - thanks!

1. Why "Derived constructor" is called but "Derived destructor" not in Case 1 since object B is new'ed from Derived class?
Probably because there is no virtual destructor and you're deleting
the object through a pointer to class 'base', not 'Derived'. HOWEVER,
this behaviour is simply a chance occurrence because if destructor is
not virtual an attempt to delete an object of a derived type through
a pointer to a base class causes _undefined_behaviour_.
2. Why "Derived destructor" is called in Case 2 since only ~base() becomes
"virtual" and ~Derived() is still non-virtual?
The "since" part of the question is false. If the base class' d-tor
is virtual, all derived class' d-tors are virtual too.
3. Does Case 3 show that we don't need any virtual destructor to make
~Derived() called?
I suppose it does. If you delete it through the pointer to Derived,
there is no need in a virtual destructor.
4. Is "virtual destructor" needed only for Case 2?
No. Case 1 requires a virtual destructor. Without it the behaviour
of the Case 1 program is _undefined_.


Case 1:
========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n"; }
};
int main(){
base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Base destructor

Case 2:
========

class base
{
public:
virtual ~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor

Case 3:
=========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

Derived* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor

Jul 19 '05 #2

P: n/a
"Stub" <st**@asof.com> wrote in message
news:Yo***********************@bgtnsc04-news.ops.worldnet.att.net...
Please answer my questions below - thanks!
I would, but this does look suspiciously like homework to me. We're all more
than happy to help with that if there's some evidence of an effort having
been made, but no-one here is going to do your homework for you. Besides,
I've got more than enough of my own... :-) FWIW, I'll explain the underlying
idea to you, and if you can figure out the answers to the questions from
that, then so much the better:

Given a base class B and a class D (publicly) derived from B, the following
scenario exhibits undefined behaviour iff B's destructor is non-virtual:

B *p = new D;
delete p;

(Interesting aside - The following always exhibits undefined behaviour,
whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)

This is essentially because the compiler merely invokes the destructor
associated with the static type of p (i.e. B's destructor) as it doesn't
realise it's supposed to invoke D's destructor. Much as:

class B
{
public:
void f() { std::cout << "B"; }
};

class D : public B
{
public:
void f() { std::cout << "D"; }
};

B *p = new D;
p->f();
....

will output B, since f is non-virtual. However, if we did:

D *q = new D;
q->f();
....

then it would output D, since q is a D *.

Hope that helped a bit, I'll let you figure out the rest for yourself.

Cheers,

Stuart.
1. Why "Derived constructor" is called but "Derived destructor" not in Case 1 since object B is new'ed from Derived class?
2. Why "Derived destructor" is called in Case 2 since only ~base() becomes
"virtual" and ~Derived() is still non-virtual?
3. Does Case 3 show that we don't need any virtual destructor to make
~Derived() called?
4. Is "virtual destructor" needed only for Case 2?
Case 1:
========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n"; }
};
int main(){
base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Base destructor

Case 2:
========

class base
{
public:
virtual ~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

base* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor

Case 3:
=========

class base
{
public:
~base() { cout << "Base destructor\n"; }
};
class Derived : public base
{
public:
Derived() { cout << "Derived constructor\n"; }
~Derived() { cout << "Derived destructor\n" }
};
int main () {

Derived* B = new Derived;
delete B;
return 0;
}

OUTPUT:
Derived constructor
Derived destructor
Base destructor

Jul 19 '05 #3

P: n/a
Victor Bazarov <v.********@comAcast.net> wrote in message
news:2Bxqb.131338$Tr4.337402@attbi_s03...
"Stub" <st**@asof.com> wrote...
Please answer my questions below - thanks!

1. Why "Derived constructor" is called but "Derived destructor" not in

Case
1 since object B is new'ed from Derived class?


Probably because there is no virtual destructor and you're deleting
the object through a pointer to class 'base', not 'Derived'. HOWEVER,
this behaviour is simply a chance occurrence because if destructor is
not virtual an attempt to delete an object of a derived type through
a pointer to a base class causes _undefined_behaviour_.
2. Why "Derived destructor" is called in Case 2 since only ~base() becomes "virtual" and ~Derived() is still non-virtual?


The "since" part of the question is false. If the base class' d-tor
is virtual, all derived class' d-tors are virtual too.

This is the part which confused me...

First, is Case 2 the right way to implement virtual destructor so that when
deleting an object of Derived type via its base class pointer, the
destructor in Derived will be called for sure?

Second, the virtual d-tor, "~base()", in base has a different function name
compared to the virtual d-tor in Derived, "~Derived()." How could it make
"~Derived()" automatically virtual here? Is there any overriding here given
their names different? Or is this just designed by C++ Standard. See, I am
just trying to understand the concept here.

Thanks for your help!
Jul 19 '05 #4

P: n/a
"Stub" <st**@asof.com> wrote...
Victor Bazarov <v.********@comAcast.net> wrote in message
news:2Bxqb.131338$Tr4.337402@attbi_s03...
"Stub" <st**@asof.com> wrote...
Please answer my questions below - thanks!

1. Why "Derived constructor" is called but "Derived destructor" not in Case
1 since object B is new'ed from Derived class?


Probably because there is no virtual destructor and you're deleting
the object through a pointer to class 'base', not 'Derived'. HOWEVER,
this behaviour is simply a chance occurrence because if destructor is
not virtual an attempt to delete an object of a derived type through
a pointer to a base class causes _undefined_behaviour_.
2. Why "Derived destructor" is called in Case 2 since only ~base() becomes "virtual" and ~Derived() is still non-virtual?


The "since" part of the question is false. If the base class' d-tor
is virtual, all derived class' d-tors are virtual too.

This is the part which confused me...

First, is Case 2 the right way to implement virtual destructor so that

when deleting an object of Derived type via its base class pointer, the
destructor in Derived will be called for sure?
Case 2 is fine.
Second, the virtual d-tor, "~base()", in base has a different function name compared to the virtual d-tor in Derived, "~Derived()."
It doesn't matter. It's a special function. Names here are not important.
How could it make
"~Derived()" automatically virtual here?
What's the difference _how_ it does that? The Standard requires it, so it
has to figure a way.
Is there any overriding here given
their names different? Or is this just designed by C++ Standard. See, I am just trying to understand the concept here.


The concept is this: for any class T if any of its base classes have their
destructor declared "virtual", the T's destructor is virtual, no ifs, ands
or buts.

Get a copy of the Standard, it's only $18 (in a PDF form). I strongly
recommend it.

Victor
Jul 19 '05 #5

P: n/a

"Stuart Golodetz" <st*************@new.ox.ac.uk> wrote in message
news:bo**********@news.ox.ac.uk...
"Stub" <st**@asof.com> wrote in message
news:Yo***********************@bgtnsc04-news.ops.worldnet.att.net...
[snip]

(Interesting aside - The following always exhibits undefined behaviour,
whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)


Stuart, you know better!. If the destructor is virtual, the code is OK.

See the code below and the output at the end

#include <iostream>

using namespace std;

class B
{
public:
virtual ~B()
{
cout << "B destructor\n";
}
};

class D : public B
{
public:
~D()
{
cout << "D destructor\n";
}
};
int main(int argc, char* argv[])
{
B *p = new D[5];

delete[] p;

return 0;
}
D destructor
B destructor
D destructor
B destructor
D destructor
B destructor
D destructor
B destructor
D destructor
B destructor

which is the right thing. (VC++ 6.0)

/dan
Jul 19 '05 #6

P: n/a
Dan Cernat wrote:
(Interesting aside - The following always exhibits undefined behaviour,
whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)
Stuart, you know better!. If the destructor is virtual, the code is OK.


No, it's not.
See the code below and the output at the end


Add a data member to D, and see what happens.

Max


Jul 19 '05 #7

P: n/a
"Max M" <ed***@maxim.comm2000.it> wrote in message
news:bo**********@newsreader.mailgate.org...
Dan Cernat wrote:
(Interesting aside - The following always exhibits undefined behaviour,
whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)


Stuart, you know better!. If the destructor is virtual, the code is OK.


No, it's not.
See the code below and the output at the end


Add a data member to D, and see what happens.


Interesting. Why is that?
--
Gary
Jul 19 '05 #8

P: n/a
Max M wrote in news:bo**********@newsreader.mailgate.org:
Dan Cernat wrote:
(Interesting aside - The following always exhibits undefined
behaviour, whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)


Stuart, you know better!. If the destructor is virtual, the code is
OK.


No, it's not.
See the code below and the output at the end


Add a data member to D, and see what happens.


Just for fun I did. I tried vc7.1 first and it worked just fine,
gcc 3.2 (MingW) seg' faulted and bcc called the B destructor 5 times
but no seg' fault (It probably would have if B's dtor had been more
complex, i.e. derefrenced this pointer in some significant way).

Clearly vc7.1 stores sizeof info in the poloymorfic type (or in its
vtable) and does "the right thing" when deleteing a polymorphic array.

AFAICT vc7.1's behaviour an extension to the standard, I really can't
imagine why they did it :).

FWIW here's the code:

#include <iostream>

using std::cerr;

int ctor = 0;

struct test
{
int data[10];

test()
{
for ( int i = 0; i < 10; ++i )
data[i] = 0x5AA55AA5;

data[1] = ++ctor;
}
~test()
{
cerr << "~test(" << data[1] << ")\n";
}
};

class B
{
public:
virtual ~B()
{
poly();
cerr << "B destructor\n";
}
virtual void poly() { cerr << "poly B\n"; }
};

class D : public B
{
test data_member;
public:
~D()
{
poly();
cerr << "D destructor\n";
}
virtual void poly() { cerr << "poly D\n"; }
};
int main()
{
B *p = new D[5];

delete[] p;
}

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 19 '05 #9

P: n/a
Max M <ed***@maxim.comm2000.it> wrote in message news:<bo**********@newsreader.mailgate.org>...
Dan Cernat wrote:
(Interesting aside - The following always exhibits undefined behaviour,
whether or not B has a virtual destructor:
B *p = new D[5];
delete [] p;
)


Stuart, you know better!. If the destructor is virtual, the code is OK.


No, it's not.
See the code below and the output at the end


Add a data member to D, and see what happens.

Max


I tried your suggestion and although I got the behaviour one would
expect (destruct of derived objects) I have to agree that it could
lead to undefined behaviour, especially if the pointer is passed
around and one tries to access its members (not at the index 0).
However, the memory manager seems that is doing the right job.

the new code:

#include <iostream>

using namespace std;

class InDerived
{
public:
~InDerived() {cout << "Destructor InDerived\n";}
};

class InBase
{
public:
~InBase() {cout << "Destructor InBase\n";}
};
class base
{
public:
InBase m_inBase;
virtual ~base() {cout << "Destructor base\n";}
};

class derived : public base
{
public:
InDerived m_inDerived;
int x;
~derived() {cout << "Destructor derived\n";}
};

void DeleteArray(base *p)
{
delete [] p;
}

int _tmain(int argc, _TCHAR* argv[])
{
base *p = new derived[5];

DeleteArray(p);
return 0;
}
Jul 19 '05 #10

P: n/a

"Dan Cernat" <dc*****@excite.com> wrote in message news:aa**************************@posting.google.c om...

I tried your suggestion and although I got the behaviour one would
expect (destruct of derived objects) I have to agree that it could
lead to undefined behaviour, especially if the pointer is passed
around and one tries to access its members (not at the index 0).
However, the memory manager seems that is doing the right job.


Not lead to undefined behavior, it is undefined behavior. Coincidentally
working is one of those insidious undefined behaviors. Try putting
some more data members in. It will be sure to break. The issue is not
just delete, you can ONLY convert the derived pointer of one object to
a base class. It doesn't work in arrays.

Image sizeof(base) is 4. sizeof(derived) is 8.
What is the relationship the base_array[1] with respect to base_array[0].
What is the relationship of derived_array[1] to derived_array[0].
Just think about it for a minute.
Jul 19 '05 #11

P: n/a

"Ron Natalie" <ro*@sensor.com> wrote in message
news:3f**********************@news.newshosting.com ...

"Dan Cernat" <dc*****@excite.com> wrote in message news:aa**************************@posting.google.c om...

I tried your suggestion and although I got the behaviour one would
expect (destruct of derived objects) I have to agree that it could
lead to undefined behaviour, especially if the pointer is passed
around and one tries to access its members (not at the index 0).
However, the memory manager seems that is doing the right job.


Not lead to undefined behavior, it is undefined behavior. Coincidentally
working is one of those insidious undefined behaviors. Try putting
some more data members in. It will be sure to break. The issue is not
just delete, you can ONLY convert the derived pointer of one object to
a base class. It doesn't work in arrays.

Image sizeof(base) is 4. sizeof(derived) is 8.
What is the relationship the base_array[1] with respect to base_array[0].
What is the relationship of derived_array[1] to derived_array[0].
Just think about it for a minute.

Yup, this is what I wanted to say in my prevoius message and I totally agree
with what you say.
I really should think twice before posting. Point taken.

Thanks,
Dan
Jul 19 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.