Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Understanding virtual destructor

41 views
Skip to first unread message

JiiPee

unread,
Oct 7, 2016, 7:03:08 PM10/7/16
to
Maybe a simple question, but just ensuring that understanding 100%.

If I create a Circle like this:

class Shape
{
public:
~Shape();
int a;
};

class Circle: public Shape
{
public:
~Shape();
int b;
};

Then:
Shape* s = new Circle();
delete s;

now Shape destructor should be virtual obviously. But what happens if I
do not make it virtual: does int b; be deleted? Am I correct that when
creating s (s = new Circle();) then it consists of two parts: Shape part
and Circle part and they are separate "units". So when I destroy s only
the Shape part of the object will be destroyed but all the Circle part
will stay in memory (leaks)? is this how it is?

So the memory is something like this:

#####################
Shape # Circle #
# #
#####################

and after delete s; it is:

##########
Circle #
#
##########


Mr Flibble

unread,
Oct 7, 2016, 8:29:31 PM10/7/16
to
On 08/10/2016 00:02, JiiPee wrote:
> Maybe a simple question, but just ensuring that understanding 100%.
>
> If I create a Circle like this:
>
> class Shape
> {
> public:
> ~Shape();
> int a;
> };
>
> class Circle: public Shape
> {
> public:
> ~Shape();
> int b;
> };

Do you have a Square class too? If so it should be derived from a
Rectangle class not a Shape class.

/Flibble

Jerry Stuckle

unread,
Oct 7, 2016, 8:38:42 PM10/7/16
to
Both a and b will be deleted. However, the destructor for b will be
called, but not the Shape (and therefore the destructor for a).

It's not a problem with int values - but if it were an object such as a
String, it could create a memory leak.

--
==================
Remove the "x" from my email address
Jerry Stuckle
jstu...@attglobal.net
==================

JiiPee

unread,
Oct 7, 2016, 8:52:31 PM10/7/16
to
? why you asking this? :)

JiiPee

unread,
Oct 7, 2016, 8:56:55 PM10/7/16
to
On 08/10/2016 01:38, Jerry Stuckle wrote:
> the destructor for b will be
> called, but not the Shape (and therefore the destructor for a).


what do you mean? b does not have a destructor. Also the Shape -class
destructor will be called. Not sure what you say ("but not the Shape")...

JiiPee

unread,
Oct 7, 2016, 9:04:01 PM10/7/16
to
On 08/10/2016 01:38, Jerry Stuckle wrote:
> b will be deleted


so whatever type b is (even if it is an object) it will be deleted but
its destructor will not be called?

Ian Collins

unread,
Oct 7, 2016, 9:11:37 PM10/7/16
to
On 10/ 8/16 12:02 PM, JiiPee wrote:
> Maybe a simple question, but just ensuring that understanding 100%.
>
> If I create a Circle like this:
>
> class Shape
> {
> public:
> ~Shape();
> int a;
> };
>
> class Circle: public Shape
> {
> public:
> ~Shape();

I assume this should be ~Circle():

> int b;
> };
>
> Then:
> Shape* s = new Circle();
> delete s;
>
> now Shape destructor should be virtual obviously. But what happens if I
> do not make it virtual: does int b; be deleted?

The Circle object will be deleted, ~Circle() will be called, but not
~Shape().

--
Ian

Jerry Stuckle

unread,
Oct 7, 2016, 9:16:50 PM10/7/16
to
b has a constructor, but for an int, it does nothing.

But sorry, I meant Circle's destructor would not be called. That's what
I get for being in a hurry.

Jerry Stuckle

unread,
Oct 7, 2016, 9:17:14 PM10/7/16
to
That is correct.

Jerry Stuckle

unread,
Oct 7, 2016, 9:18:22 PM10/7/16
to
Ian, that's where I was also long due to being in a hurry. Shape's
destructor will be called, because the program is deleting a Shape. But
Circle's destructor will not be called.

Alf P. Steinbach

unread,
Oct 7, 2016, 9:30:28 PM10/7/16
to
On 08.10.2016 01:02, JiiPee wrote:
> Maybe a simple question, but just ensuring that understanding 100%.
>
> If I create a Circle like this:
>
> class Shape
> {
> public:
> ~Shape();
> int a;
> };
>
> class Circle: public Shape
> {
> public:
> ~Shape();
> int b;
> };
>
> Then:
> Shape* s = new Circle();
> delete s;
>
> now Shape destructor should be virtual obviously. But what happens if I
> do not make it virtual: does int b; be deleted?

Formally it's Undefined Behavior, and that means that anything or
nothing may happen.

In practice a compiler is unlikely to detect the UB and then make the
program do Nasty Things™ in that case.

Instead the ordinary code for a `delete` expression is likely to be
executed, under the invalid assumption that the object to be deleted is
of most derived class `Shape`. Since a `Shape` object is of smaller size
than the actual `Circle` object, this might foul up things with respect
to the memory allocator. Or not: it depends on how the implementation
stores the sizes of allocated objects, or not, and it depends on whether
the memory block that would have been allocated for a `Shape` would be
the same size as for a `Circle`.

In the common case where it's the memory manager that keeps track of
block size for all dynamically allocated objects, the code is likely to
just work.

That is one possible effect of Undefined Behavior.


> Am I correct that when
> creating s (s = new Circle();) then it consists of two parts: Shape part
> and Circle part and they are separate "units".

They're not separate: they're very much adjoined, and in some cases
parts of a derived class may be intermingled with parts of a base class.


> So when I destroy s only
> the Shape part of the object will be destroyed but all the Circle part
> will stay in memory (leaks)? is this how it is?

No no no. :)

There's one allocated block of memory. It may or may not be correctly
dellocated, depending on the C++ implementation. Before that only the
`Shape` destructor will be executed, since it's not virtual.


Cheers & hth.,

- Alf

JiiPee

unread,
Oct 7, 2016, 9:39:16 PM10/7/16
to
On 08/10/2016 02:16, Jerry Stuckle wrote:
> b has a constructor, but for an int, it does nothing.


but you were talking about destructors. does int also have a destructor?

JiiPee

unread,
Oct 7, 2016, 9:40:27 PM10/7/16
to
On 08/10/2016 02:17, Jerry Stuckle wrote:
> On 10/7/2016 9:03 PM, JiiPee wrote:
>> On 08/10/2016 01:38, Jerry Stuckle wrote:
>>> b will be deleted
>>
>> so whatever type b is (even if it is an object) it will be deleted but
>> its destructor will not be called?
>>
> That is correct.
>

oh ok, this was a bit new to me.. never thought of this. sometimes good
to think deeper :)

JiiPee

unread,
Oct 7, 2016, 9:41:54 PM10/7/16
to
On 08/10/2016 02:11, Ian Collins wrote:
> On 10/ 8/16 12:02 PM, JiiPee wrote:
>> Maybe a simple question, but just ensuring that understanding 100%.
>>
>> If I create a Circle like this:
>>
>> class Shape
>> {
>> public:
>> ~Shape();
>> int a;
>> };
>>
>> class Circle: public Shape
>> {
>> public:
>> ~Shape();
>
> I assume this should be ~Circle():


yes, copy-paste error

>
>

JiiPee

unread,
Oct 7, 2016, 9:53:19 PM10/7/16
to
On 08/10/2016 02:28, Alf P. Steinbach wrote:
>
> Formally it's Undefined Behavior, and that means that anything or
> nothing may happen.

Ok, you got me a bit confused as Jerry said that b would be deleted.
hmmm... so you are saying its not certain.
So basically this is a bug/error and in theory crash the program.

>
>> Am I correct that when
>> creating s (s = new Circle();) then it consists of two parts: Shape part
>> and Circle part and they are separate "units".
>
> They're not separate: they're very much adjoined, and in some cases
> parts of a derived class may be intermingled with parts of a base class.

ok, good. this is what I really wanted to know. So they can be mixed.
Then it makes sense that not part of the memory cannot be deleted, but
its all or nothing.

>
>
>> So when I destroy s only
>> the Shape part of the object will be destroyed but all the Circle part
>> will stay in memory (leaks)? is this how it is?
>
> No no no. :)
>
> There's one allocated block of memory.

ok and the memory manager does not know which part belongs to Shape and
which to Circle.

> It may or may not be correctly dellocated, depending on the C++
> implementation.

so this means b might be left hanging in the memory ( a leak).

JiiPee

unread,
Oct 7, 2016, 9:54:45 PM10/7/16
to
On 08/10/2016 01:38, Jerry Stuckle wrote:
> Both a and b will be deleted.


hmm, but now Alf is saying it can be an undefined behaviour..... which
would mean b is not necessary deleted in the worst case scenario... ?
Please check Alf s post

mark

unread,
Oct 7, 2016, 10:16:11 PM10/7/16
to
On 2016-10-08 03:28, Alf P. Steinbach wrote:
>>
>> now Shape destructor should be virtual obviously. But what happens if I
>> do not make it virtual: does int b; be deleted?

> Formally it's Undefined Behavior, and that means that anything or
> nothing may happen.

C++14, 5.3.5 Delete [expr.delete]:
<<<
In the first alternative (delete object), if the static type of the
object to be deleted is different from its dynamic type, the static type
shall be a base class of the dynamic type of the object to be deleted
and the static type shall have a virtual destructor or the behavior is
undefined.
...
>>>

> In practice a compiler is unlikely to detect the UB and then make the
> program do Nasty Things™ in that case.

The detection is possible in quite a few cases. I'm not nearly as
optimistic as you about the compiler not doing Nasty Things. E.g. it
might come to the invalid conclusion that certain objects cannot alias
and make optimizations based on that (compilers use undefined behavior
extensively to enable optimizations).

Richard

unread,
Oct 8, 2016, 12:03:17 AM10/8/16
to
[Please do not mail me a copy of your followup]

"Alf P. Steinbach" <alf.p.stein...@gmail.com> spake the secret code
<nt9i6s$scv$1...@dont-email.me> thusly:

>Formally it's Undefined Behavior, and that means that anything or
>nothing may happen.

I like it when undefined behavior means gcc invokes nethack.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Bo Persson

unread,
Oct 8, 2016, 3:48:34 AM10/8/16
to
On 2016-10-08 03:53, JiiPee wrote:
> On 08/10/2016 02:28, Alf P. Steinbach wrote:
>>
>>> So when I destroy s only
>>> the Shape part of the object will be destroyed but all the Circle part
>>> will stay in memory (leaks)? is this how it is?
>>
>> No no no. :)
>>
>> There's one allocated block of memory.
>
> ok and the memory manager does not know which part belongs to Shape and
> which to Circle.

The memory manager knows nothing about objects, it only handles blocks
of memory (bytes).

>
>> It may or may not be correctly dellocated, depending on the C++
>> implementation.
>
> so this means b might be left hanging in the memory ( a leak).
>

No.

It might happen that the memory manager only handles some block sizes,
like 8 or 16 bytes minimum. In that case both your types will have the
same size allocated. Then it "works".

Or it might be the memory manager that (by some magic) knows the sizes
of all allocated memory blocks. Then it can always deallocate them properly.

Or it might be the compiler that tells it to deallocate sizeof(Shape)
bytes. Then the heap will likely be damaged, and the Bad Things will
happen shortly after.


Bo Persson

Jerry Stuckle

unread,
Oct 8, 2016, 6:08:20 AM10/8/16
to
Yes, it does. Conceptually, all objects in C++ have destructors, just
like all objects have constructors. In the case of an int, it does
nothing. But the concept is still there.

Jerry Stuckle

unread,
Oct 8, 2016, 6:11:45 AM10/8/16
to
b is always deleted, because the containing object is deleted. Bit its
destructor is not called.

JiiPee

unread,
Oct 8, 2016, 6:24:14 AM10/8/16
to
On 08/10/2016 11:11, Jerry Stuckle wrote:
> On 10/7/2016 9:54 PM, JiiPee wrote:
>> On 08/10/2016 01:38, Jerry Stuckle wrote:
>>> Both a and b will be deleted.
>>
>> hmm, but now Alf is saying it can be an undefined behaviour..... which
>> would mean b is not necessary deleted in the worst case scenario... ?
>> Please check Alf s post
>>
> b is always deleted, because the containing object is deleted. Bit its
> destructor is not called.
>

ok, so the memory manager have no error in knowing the size of the
object to delete even though we delete it via the base class pointer? So
it will always delete the same size of memory block whether we delete
via the base class pointer or via the derived class pointer? So the
undefined behaviour has nothing to do with deleting the objects memory
block?

Öö Tiib

unread,
Oct 8, 2016, 8:08:25 AM10/8/16
to
On Saturday, 8 October 2016 04:53:19 UTC+3, JiiPee wrote:
> On 08/10/2016 02:28, Alf P. Steinbach wrote:
> >
> > Formally it's Undefined Behavior, and that means that anything or
> > nothing may happen.
>
> Ok, you got me a bit confused as Jerry said that b would be deleted.
> hmmm... so you are saying its not certain.
> So basically this is a bug/error and in theory crash the program.

Of course it may crash the program. It is not even some "theoretical"
crash that in practice never happens. Quite similar example that crashes
with gcc:

#include <iostream>

struct Shape { ~Shape() {}; int a; };

struct Grape
{
virtual // <- erase or comment it out and the program will crash
~Grape() {}; int g;
};

struct Crashy: public Shape, public Grape { int n; };

int main()
{
Grape* g = new Crashy();
delete g; // <- it will crash here
std::cout << "quod erat demonstrandum\n";
}

>
> >
> >> Am I correct that when
> >> creating s (s = new Circle();) then it consists of two parts: Shape part
> >> and Circle part and they are separate "units".
> >
> > They're not separate: they're very much adjoined, and in some cases
> > parts of a derived class may be intermingled with parts of a base class.
>
> ok, good. this is what I really wanted to know. So they can be mixed.
> Then it makes sense that not part of the memory cannot be deleted, but
> its all or nothing.

That is so when objects do not contain member objects that further manage
more dynamic memory on their own. For example 'std::vector' member.

>
> >
> >
> >> So when I destroy s only
> >> the Shape part of the object will be destroyed but all the Circle part
> >> will stay in memory (leaks)? is this how it is?
> >
> > No no no. :)
> >
> > There's one allocated block of memory.
>
> ok and the memory manager does not know which part belongs to Shape and
> which to Circle.
>
> > It may or may not be correctly dellocated, depending on the C++
> > implementation.
>
> so this means b might be left hanging in the memory ( a leak).

No. It *is* undefined behavior. Compiler usually does not produce diagnostics (but may) and does not refuse to compile it (but may) and does produce
running executable (but may not). When that executable is ran then
what happens may differ by compiler version or compiling options and
may be predictable or may be is not predictable.

That all may be painful and may waste time of everybody so deliberately
invoking undefined behavior for some effect may be considered as sabotage
by your colleagues.

Paavo Helde

unread,
Oct 8, 2016, 11:38:55 AM10/8/16
to
In practice, probably yes, AFAIK the common memory allocators are able
to release memory blocks by the address only, without the need to
provide the size information (and consequently, many memory allocators
even provide a function for returning the (effective) size of an
allocated block by its address).

So in practice the memory will probably get deallocated correctly, but
the destructor of the derived class and its members are not called. In
case of your original example this does not matter because the int
destructor is a non-op anyway. However, this is still formally undefined
behavior (and may easily become a source of real bugs when something
more interesting is added in derived classes).

Cheers
Paavo

Jerry Stuckle

unread,
Oct 8, 2016, 5:11:49 PM10/8/16
to
That is true. You have one object, not two. The derived class object
can be considered simply an extension of the base class object.

And the memory routines don't know even the size of the object. All it
knows is the size of the block of allocated memory, which is at least
the size of the object (and can be larger - but that's another story).

When an object gets deleted, either via delete or going out of scope,
the C++ compiler generates the code to call the destructor. If the
destructor is not virtual, the call will be to the apparent type of the
object (Shape, in this case). Destructors in any base classes will
still be called, but not destructors in derived classes.

When you have a virtual destructor the destructor is called for the real
type (Circle in this case) instead of the apparent type. And, of
course, destructors for all base classes will still be called.

And, of course, it is the responsibility of the class destructor to
destroy any objects which require destruction.

Richard Damon

unread,
Oct 9, 2016, 6:22:13 PM10/9/16
to
On 10/7/16 7:02 PM, JiiPee wrote:
>
> now Shape destructor should be virtual obviously. But what happens if I
> do not make it virtual: does int b; be deleted? Am I correct that when
> creating s (s = new Circle();) then it consists of two parts: Shape part
> and Circle part and they are separate "units". So when I destroy s only
> the Shape part of the object will be destroyed but all the Circle part
> will stay in memory (leaks)? is this how it is?

Technically, without the destructor being virtual, doing a delete via a
base pointer for a derived class is undefined behavior, so anything is
allowed to happen.

Generally, what does happen is that only the base class destructor gets
called (and not any of the derived classes), which wouldn't make a
difference in your case, but could if any of the derived classes have
non-trivial destructors. The other possible issue is that if the
compiler can be sure that operator new hasn't been overridden, it might
use a small object optimization for the new, and put the object back to
the wrong small object heap.

0 new messages