C++ virtual functions
Virtual functions allow C++ to choose the function implementation using the real object type at runtime.
This is called runtime polymorphism.
Without virtual, C++ uses the pointer/reference type.
With virtual, C++ uses the real object type.
The problem
Suppose we have a base class and a derived class.
Output:
Why does the second call print animal sound?
Because animal is an Animal&, and speak() is not virtual.
So C++ calls Animal::speak().
Add virtual
Add virtual to the base class function.
Output:
Now C++ checks the real object type.
The reference type is Animal&, but the real object is Dog.
So C++ calls Dog::speak().
Use override
Use override in the derived class.
override tells the compiler:
this function must override a virtual function from the base class.
This protects you from mistakes.
Example mistake:
If the base function is:
then the derived function must also be:
Base pointer to derived object
Virtual functions are most useful with base pointers or base references.
The pointer type is Animal*.
The real object is Dog.
Because speak() is virtual, Dog::speak() is called.
Heap allocation
You can also allocate the derived object on the heap and store it in a base pointer.
This line creates a Dog, but stores it behind an Animal*.
Because speak() is virtual, calling:
calls Dog::speak().
Virtual destructor
If you delete a derived object through a base pointer, the base class should have a virtual destructor.
Why?
The pointer type is Animal*, but the real object is Dog.
A virtual destructor makes sure the correct destructor chain runs.
Simple rule:
If a class has virtual functions, give it a virtual destructor.
Smart pointer example
Prefer smart pointers over raw new and delete.
std::unique_ptr<Animal> owns the object.
The real object is Dog.
When animal goes out of scope, the object is deleted automatically.
Because Animal has a virtual destructor, deletion is correct.
Quick rules
| Situation | Use |
|---|---|
| Function should behave differently for derived classes | virtual |
| Derived function overrides base virtual function | override |
| Delete derived object through base pointer | virtual destructor |
| Own polymorphic object | std::unique_ptr<Base> |
| Want runtime polymorphism | base pointer or base reference |
What is not covered here
This post does not cover abstract classes or interfaces.
Those use pure virtual functions:
That should be learned in a separate post.