Virtual destructors
Although C++ provides a default destructor for your classes if you do not provide one yourself, it is sometimes the case that you will want to provide your own destructor (particularly if the class needs to deallocate memory). You should always make your destructors virtual if you’re dealing with inheritance. Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> class Base { public: ~Base() // note: not virtual { std::cout << "Calling ~Base()\n"; } }; class Derived: public Base { private: int* m_array; public: Derived(int length) : m_array{ new int[length] } { } ~Derived() // note: not virtual (your compiler may warn you about this) { std::cout << "Calling ~Derived()\n"; delete[] m_array; } }; int main() { Derived *derived { new Derived(5) }; Base *base { derived }; delete base; return 0; } |
Note: If you compile the above example, your compiler may warn you about the non-virtual destructor (which is intentional for this example). You may need to disable the compiler flag that treats warnings as errors to proceed.
Because base is a Base pointer, when base is deleted, the program looks to see if the Base destructor is virtual. It’s not, so it assumes it only needs to call the Base destructor. We can see this in the fact that the above example prints:
Calling ~Base()
However, we really want the delete function to call Derived’s destructor (which will call Base’s destructor in turn), otherwise m_array will not be deleted. We do this by making Base’s destructor virtual:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> class Base { public: virtual ~Base() // note: virtual { std::cout << "Calling ~Base()\n"; } }; class Derived: public Base { private: int* m_array; public: Derived(int length) : m_array{ new int[length] } { } virtual ~Derived() // note: virtual { std::cout << "Calling ~Derived()\n"; delete[] m_array; } }; int main() { Derived *derived { new Derived(5) }; Base *base { derived }; delete base; return 0; } |
Now this program produces the following result:
Calling ~Derived() Calling ~Base()
Rule: Whenever you are dealing with inheritance, you should make any explicit destructors virtual.
As with normal virtual member functions, if a base class function is virtual, all derived overrides will be considered virtual regardless of whether they are specified as such. It is not necessary to create an empty derived class destructor just to mark it as virtual.
Virtual assignment
It is possible to make the assignment operator virtual. However, unlike the destructor case where virtualization is always a good idea, virtualizing the assignment operator really opens up a bag full of worms and gets into some advanced topics outside of the scope of this tutorial. Consequently, we are going to recommend you leave your assignments non-virtual for now, in the interest of simplicity.
Ignoring virtualization
Very rarely you may want to ignore the virtualization of a function. For example, consider the following code:
1 2 3 4 5 6 7 8 9 10 11 |
class Base { public: virtual const char* getName() const { return "Base"; } }; class Derived: public Base { public: virtual const char* getName() const { return "Derived"; } }; |
There may be cases where you want a Base pointer to a Derived object to call Base::getName() instead of Derived::getName(). To do so, simply use the scope resolution operator:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { Derived derived; const Base &base { derived }; // Calls Base::GetName() instead of the virtualized Derived::GetName() std::cout << base.Base::getName() << '\n'; return 0; } |
You probably won’t use this very often, but it’s good to know it’s at least possible.
Should we make all destructors virtual?
This is a common question asked by new programmers. As noted in the top example, if the base class destructor isn’t marked as virtual, then the program is at risk for leaking memory if a programmer later deletes a base class pointer that is pointing to a derived object. One way to avoid this is to mark all your destructors as virtual. But should you?
It’s easy to say yes, so that way you can later use any class as a base class -- but there’s a performance penalty for doing so (a virtual pointer added to every instance of your class). So you have to balance that cost, as well as your intent.
Conventional wisdom (as initially put forth by Herb Sutter, a highly regarded C++ guru) has suggested avoiding the non-virtual destructor memory leak situation as follows, “A base class destructor should be either public and virtual, or protected and nonvirtual.” A class with a protected destructor can’t be deleted via a pointer, thus preventing the accidental deleting of a derived class through a base pointer when the base class has a non-virtual destructor. Unfortunately, this also means the base class can’t be deleted through a base class pointer, which essentially means the class can’t be dynamically allocated or deleted except by a derived class. This also precludes using smart pointers (such as std::unique_ptr and std::shared_ptr) for such classes, which limits the usefulness of that rule (we cover smart pointers in a later chapter). It also means the base class can’t be allocated on the stack. That’s a pretty heavy set of penalties.
Now that the final specifier has been introduced into the language, our recommendations are as follows:
- If you intend your class to be inherited from, make sure your destructor is virtual.
- If you do not intend your class to be inherited from, mark your class as final. This will prevent other classes from inheriting from it in the first place, without imposing any other use restrictions on the class itself.
![]() |
![]() |
![]() |
sorry for asking guys but as we are going deeper and deeper in the tutorial
its getting more difficult to follow.
i have placed some questions in the code below and hope someone could help clear them out
thanks
1. Feedback
The error from Microsoft Visual Studio 2019 does not point towards the lines without "virtual".
"Severity Code Description Project File Line Suppression State
Error MSB3491 Could not write lines to file "Debug\Chapter1.6fa4fbab.tlog\Chapter18.4VirtualDestructorsAssignmentOverridingVirtualization.lastbuildstate". Path: Debug\Chapter1.6fa4fbab.tlog\Chapter18.4VirtualDestructorsAssignmentOverridingVirtualization.lastbuildstate exceeds the OS max path limit. The fully qualified file name must be less than 260 characters. Chapter18.4VirtualDestructorsAssignmentOverridingVirtualization C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets 354 "
2. Feedback on 2nd example
The examples will not compile in Microsoft Visual Studio 2019.
"You should always make your destructors virtual if you’re dealing with inheritance."
Is the above statement a hidden rule?
It's not in one of the ruleboxes.
>>Unfortunately, this also means the base class can’t be deleted through a base class pointer, which essentially means the class can’t be dynamically allocated or deleted except by a derived class.
Is this the consequence of having a protected non-virtual Base destructor?
Would you please give a code example about this, if it is possible? It is very confusing!
>>which essentially means the class can’t be dynamically allocated or deleted except by a derived class.
But I was able to dynamically allocate memory:
Not being able to delete the class is a result of having a protected destructor. No matter if virtual or not. You posted the example yourself, `Base::~Base()` is protected, therefore it cannot be deleted unless you delete it in a derived class which has access to the destructor.
Allocating such a class is possible indeed, I've updated the sentence so that it only says the class cannot be deleted.
The rule of this lesson is not rendered in the green box that it usually is.
Will the `int n` of b ever get deallocated? Is it even allocated in the first place?
`n` gets allocated when you create `b`, but it gets leaked because you never `delete` the full object, you're only deleting the `a` portion.
"It also means the base class can’t be allocated on the stack."
I'm not sure I understand this, aren't all normal objects allocated on the stack? Do you mean 'can't be allocated on the heap'? However, you already said they can't be dynamically allocated, and a derived class could certainly be allocated on the heap which implies the base class sub-object would also be allocated there.
You could test it out yourself with a simple program. In fact, I did the same test earlier to get a better understanding, and found that the base class can’t be allocated on the stack.
Haha, the turntables!
The point of the protected destructor is to prevent destruction of of a derived object through a base pointer. We want to allow destruction of the base through a derived pointer, so the sub-object of a dynamically-allocated derived object is cool. The derived object is allowed to destruct the base portion, because it has access to protected members.
Fair play, I tested it as well. When I tried to make a temporary object in a derived class, where the parent class has a protected destructor, my compiler said "protected destructor can only be used to destroy a base class subobject". In other words, a class with a protected destructor can _only_ be inherited, and never instantiated on its own. Does that seem accurate?
Yes
Hi,
Suppose we have an inheritance chain like A -> B -> C -> D, where C dynamically allocate some resource. Then, we set a virtual destructor for C, but keep the rest as default. If we do something like
would that successfully deallocate the memory? My thought is that only destructors of C and D would be declared virtual, while B would have the non-virtual one, hence fail to resolve to D's destructor. If this is indeed the case, does that mean we have to declare an empty virtual constructor on A (most base class)?
You could test it out yourself with a simple program. In fact, I did the same test earlier to get a better understanding, and found that (e.g. with your example) 'delete ptr' will call the implicit destructor B::~B(). If B::~B() is implicit, or explicit and non-virtual, then it won't resolve to the destructor D::~D().
This reality lines up with the best practices recommended at the end of this chapter. Either:
- make your classes 'final' so they can't be inherited from, which might cause memory leaks if the destructor isn't made virtual for performance reasons
- or make your destructors 'virtual' to ensure memory safety, even if the destructor is empty
Hello, I compiled the code from this lesson
gave me these errors :
what does this mean, and how can i solve this?
Those are warnings which your compiler is treating as errors (That's a good setting). Either temporarily disable "treat warnings as errors" in your compiler settings or do as the compiler says and add the missing functions.
After reading the message several times, what i understood is that because Derived is utilizing pointer data member, so compiler is complaining about assignment and copy constructors to avoid shallow copy of object.
I fixed it by adding these two lines now it compiles fine without any error/warnings!
correct me if i am wrong :)
That's right! The problem with the shallow copy would be that both the original object and the copy try to delete `m_array` when they die
Regarding this example -
I understand why it doesn't work with non-virtual destructor, but I don't understand why anyone would use "delete base;" here instead of "delete derived;", since it's the derived pointer that had memory allocated to it. While it may be for the sake of example, I wonder if there are coding situations where this issue occurs more naturally.
If you have mixed derived types stored in a container. The container can only store the Base type, because otherwise we wouldn't be able to mix types.
In the example explaining the drawback of not making the destructor virtual, I can see how that could lead to a leaking memory through failure to proceed to an inherited class destructor.
However, I couldn't understand the initialization of the the derived pointer int the main function:
If the constructor of Derived accepts an int only to generate a pointer to an int array, why did we initialize the derived as a pointer and used new inside the initializer with the Derived constructor?
It doesn't matter what `Derived` does, the example would work the same with a constructor without parameters. We have to allocate `Derived` dynamically so that we can `delete` it later.
Got it! Thank you.
The last paragraph with Herb Sutter went over my head .Can you please give some example.
When working with classes that contain virtual functions you need to keep two things in mind:
(You'll learn about all of this in lesson "The virtual table")
(1) The size of every class that defines or overrides a virtual function is increased by a the size of a pointer.
(2) Every time a virtual function is called, the program takes LONGER to call that function than if it were a normal function, because it needs to look up which exact version of the function it needs to call, and thus it is a performance hit.
Because of these two reasons, you're gonna want to keep the number of classes that use a virtual pointer as low as possible.
But of course, there will be times when you need to use virtual pointers, especially when it comes to destructors, as this lesson teaches.
(quick recap in my own words)
Let's say you have a class "Parent"...
Now, this is all well and good. But what if someone who is using your code, wanted to make his own type of Parent, (and they would therefore make a subclass, inhereting from Parent), it could look like this:
Now, unlike with Parent, it's actually important a Child's destructor specifically is run when that Child object is deleted, as it removes dynamically allocated memory.
This causes problems when you delete a Child via a Parent*.
Now you probably got this much already, but this is where protected and public come into play:
If Parent's destructor had been protected, the line
would not have been allowed, because the outside world couldn't touch a Parent's destructor.
So, this also means a Child cannot be deleted via a Parent pointer, because the program thinks it's dealing with a Parent whose destructor is hidden, which means that the whole is-it-a-Parent-or-a-Child--which-destructor-do-I-use problem would never occur.
But still, there are occasions on which code on the outside does need to delete a Child via a Parent pointer, so then you HAVE TO make Parent::~Parent() public.
Therefore you always have two options:
(1) The destructor needs to be public.
In that case, in order to prevent a Child being wrongly deleted by a Parent*, you make the destructor virtual, so that the program will look up which destructor to use, which takes time.
(2) The destructor does not need to be public.
In that case, since Child and Parent take care of their own deletion (somehow...), and the mixup with the pointers can't happen because a Parent (or what is thought to be a Parent) can't be deleted anyway, you don't have to make a the destructor virtual.
And for the part about shared and unique pointers, you'll learn that in a later lesson. I reckon you don't need to worry about that now.
The line "It also means the base class can’t be allocated on the stack."
is about this:
A protected destructor comes with another disadvantage:
Since all objects need to be destructed eventually (they go out of scope, the program ends), this means every object's destructor needs to be called at some point. But if it's protected, the object can't be deleted. Therefore, you cannot construct an object with a protected/private destructor.
(Although, you COULD do it using 'new'. However, you still couldn't use 'delete', which you'd also want to use eventually.)
Thanks , understood 80% . b'Coz I never used protected class.
Hi Alex & nascardirver, one question I surprisingly couldn't really find the answer to anywhere in this chapter is (if there is, my mistake, please direct me to it):
Is it possible and correct/safe to delete a dynamically allocated instance of a sub-class via a super-class pointer?
And: is the answer to this question different when SuperParent and SubChild are in the virtual table?
You're deleting an object with automatic storage duration. Line 15 should be
You can check this yourself by adding destructors
Only the parent gets deleted, there's no way for the compiler to know the real type of `ptr` and there is no run-time type information.
To fix this, add a virtual destructor to your parent class (All children will get a virtual destructor automatically)
Now the children will be destructed properly.
In one of the earlier lessons, it was said that the destructor of derived class is executed and then that of base class. So shouldn't the derived class destructor be executed first irrespective of the fact that the base class destructor is virtual or not?
If you `delete` a `Base` pointer to a `Derived` class, the `delete` doesn't know what the type of the pointer actually is, it only sees `Base`, so that's what it will delete, the `Derived` portion gets leaked. If you make the destructor `virtual`, the `delete` will look up the destructor in the vtable and call the `Derived` destructor.
I am working with g++ 4.4.7:
I ran the program given in this with some modification and crash. I tried valgrind but does not seem to understand it thoroughly. Could you please help have some highlight on it
g++4 is terribly old. Consider updating.
You can only `delete` objects that you created with `new`. You're trying to `delete` `i`, which has automatic storage duration and died before you tried to `delete` it. See lesson P.6.9.
hey! nascarddriver said this wont work but its working in codeblocks why so?
p.s.destructor in base class is virtual
What I meant by "that won't work" is that it causes undefined behavior. It might not cause a problem when you run it once or twice, but when you run it for the 1000th time it might play loony tunes. See lesson 1.6 for undefined behavior.
sorry i didn't pay attention on undefined behaviour statement of yours,
thanks by the way.
Hi. I have a question below.
Thanks a lot!
If the classes are using virtual destructors, line 6 will clear up everything.
If the destructors are non-virtual, you're leaking everything that's in `Derived` but not in `Base`.
I didn't look it up, but I'm fairly certain that accessing the derived portion after deleting the base portion is undefined behavior.
Line 9 is a double free (You're trying to delete deleted memory), that won't work.
<<As with normal virtual member functions, if a base class function is virtual, all derived overrides will be considered virtual regardless of whether they are specified as such ..
>>
Maybe you meant: <<.., if a base class destructor is virtual, all>> It makes more sense to me in that context.
Keep it up!!
This applies to all virtual functions, not just destructors.
Hi Alex!
Snippet 1 line 32 has an extra space.
This lesson isn't using brace initialization.
Thanks for pointing this out, Nas. Lesson updated.