In this video, we'll talk about virtual destructors.
In the previous example,
we got four warning messages when we compiled our simple account class hierarchy.
These warnings were the compiler telling us
that deleting a polymorphic object
that doesn't have a virtual destructor could lead to unexpected behavior.
In fact, if we have a derived class object
that's destroyed by deleting its base class pointer
and that class does not have a virtual destructor,
then the behavior is undefined by the c++ standard.
That's not good.
Remember,
derived objects must always be destroyed in the correct
order and starting at the correct destructor.
So how can we address these warnings.
Thankfully, the solution is simple and intuitive.
If we have a class and that class has virtual functions,
then that class must always provide a virtual destructor.
Once we provide a virtual destructor in the base class,
then all destructors in the derived classes are also virtual.
We don't have to provide the virtual keyword again, although it's best practice to do so.
Here is the code snippet for the account base class.
Since this class has a virtual function withdraw,
then we must declare the destructor to be virtual as well.
That's it. Simple solution to a potentially serious situation.
Let's head over to the IDE and see what happens on my system
when we don't provide virtual destructors, and then we'll add them and see the difference.
Welcome back. So I'm in the IDE. I'm in the section 16 workspace
in the VirtualDestructors project.
So we've got the same account class we've had all along.
And what I've done here is I've added destructors.
Now these are not virtual destructors yet, they're just plain old destructors.
So you can see here's the destructor for the account class,
and it's simply printing out account destructor.
The checking account displays checking destructor, savings instructor
and trust destructor. Okay.
And now we've got our same example that we've been using.
We're creating four pointers to a base class object.
We're creating an account, a savings account, a checking account,a trust account dynamically on the heap.
And then we're calling the withdraw method.
All that will work perfectly fine right now.
The problem comes in right here when we delete those pointers, right.
We're going to free the storage for them. And when we build this project,
we're going to run into the same four warnings that we had before.
The warnings are about the leading object of polymorphic class type account,
which has non-virtual destructor might cause undefined behavior.
It sounds pretty ominous, and it's a potentially serious warning actually.
All the compiler knows is that we've got pointers to account objects.
So it really doesn't know which destructor to call.
And this is undefined in the c++ standard.
If we run this, we'll get one behavior on this compiler.
And this is pretty standard. You can see what's happening here when I'm calling the delete
right here on the four pointers,
we're getting account, destructor account, destructor.
So you can see that the base class destructor is being called.
Now that's a real problem because
p1 -- that's okay for p1, right. P1 is an account,
but p2 right now points to a savings account, p3 is a checking,
p4 is a trust.
So there could be a lot of memory leaks going on.
Those destructors maybe are closing files
and writing buffers out, and none of that stuff is being called.
So potentially, we could have a serious problem here.
Thankfully, the fix is really easy. All we need to do is provide
virtual destructors. And it's really easy to do. We can just use the virtual keyword
right in front of the destructor.
And we only have to do it here in the base class.
But as I mentioned, it's good practice to do it everywhere.
So there are my virtual destructors. Let me just finish up,
one more to go, and here we go.
Now if we run this -- now if we build and run,
you'll see we get no errors. And now watch what happens, this is more like it, right.
Here we're deleting p1, which calls the account destructor as before.
Now when we delete p2, p2 is a savings.
So you can see that the savings destructor is being called followed by
the base class destructor, which is exactly what we want.
So if we're freeing memory in those
destructors, if we're closing files, writing buffers, taking care of resources,
now everything's going to work as we want. You can see p3 here
is doing the checking destructor followed by the account destructor and then following
the trust destructor and the account destructor for p4.
Remember, we're only going one deep here in the inheritance hierarchy.
If you're going 5 6 10 deep,
then you've got a lot of destructors that weren't called.
They will be called now.
They'll take care of memory, take care of resources and so forth.
The rule is really, really simple.
If you've got a class and it's got a virtual function,
give it a virtual destructor as well.
Sometimes students ask about virtual constructors,
you can't have virtual constructors.
They're not allowed, and they really don't make any sense at all.
Okay. So that's it about virtual destructors.
In the next video, what we'll do is we'll talk about the override specifier
that gets the compiler to help us out just in case we make some errors.