Skip to main content

Section 19.7 Virtual Functions

Subsection 19.7.1 Virtual Functions - How

In the last section, we encountered an issue with makeIntroduction(const Person& p). When it called p.introduce(), it will always use Person::introduce(), even if p is actually a Student. We would like makeIntroduction to always use the most appropriate version of introduce. If we pass it a Student, we would hope that it would call Student::introduce() instead of the Person version.
This behavior, where the same code treats upcasted objects differently according to their actual type is known as polymorphism. The word Polymorphism literally means โ€œmany formsโ€ (โ€œpoly-โ€ for many, โ€œmorphโ€ for form). In object-oriented programming, it refers to the idea of having a common interface (like introduce()) result in many different behaviors.
In C++, virtual functions are used to achieve polymorphism. They do so by modifying the code the compiler generates for the function. For virtual functions, the compiler generates code for the function call to examine the object at runtime in order to decide which function version to use.

Aside

To make a function virtual, we add the virtual keyword before the functionโ€™s declaration. Something like:
virtual void introduce() const { ... }
Technically, this only needs to be added to the base class version. Doing so automatically makes the function virtual in any derived classes. But it can be helpful to also use virtual in the derived classes just to clearly indicate that the function is virtual.
This version of our program adds the virtual keyword to the two versions of the introduce() functions Other than that, nothing has been changed. However, after that simple change, the makeIntroduction function now behaves in a polymorphic fashion. Try it out and pay attention the the version of .introduce() used for Alex.
Listing 19.7.1.

Insight 19.7.1.

Think of virtual as meaning โ€œa function we will wait to pick the best available version of at runtimeโ€.

Subsection 19.7.2 Virtual Functions - When

So what is the downside? Why is this not always done?
The main downside is a very slight performance hit. Code to look up the right object at runtime involves more steps that assumes we always will just run one version of the code. This performance difference is tiny. But a tiny cost paid thousands of times can add up. And if you are writing code that needs to be as fast as possible, you may not want to pay that cost.
One of the design goals of C++ is to let the programmer make decisions about when they care more about performance. So C++ requires us to say โ€œyes please make this function virtualโ€. Other programming languages (Python, Javascript, etc...) often make every function virtual and do not require the programmer to specify they want polymorphic behavior (or allow them to opt out of it).
So when should you use virtual? It is generally advisable to use virtual functions when you expect that your class will be subclassed and you want to allow derived classes to provide specific implementations of a function.
Here, .getName() is not virtual as we donโ€™t expect subclasses to want to provide a โ€œbetterโ€ version of how to get the name of a Person. And the constructor canโ€™t be made virtual - Student will never provide a better way to make a plain Person (it will provide a way to make a Student that happens to be a Person).
However, we could make .getName() virtual. Try adding virtual before getName in Person. Nothing will happen other than the code will run an infinitesimal bit slower.

Insight 19.7.2.

Better safe and slightly expensive than wrong and โ€œfastโ€ (code isnโ€™t really fast if it does the wrong thing). Feel free to add virtual to every function in a class that will be inherited. And if you donโ€™t know if a class will be inherited from, assume it will be.

Checkpoint 19.7.1.

Which are the correct statement about virtual?
  • It says to decide what version of the function to call at runtime.
  • It slightly impacts performance of the function.
  • It says to decide what version of the function to call at compile time.
  • Incorrect. That is the behavior for non virtual functions.
  • Any child class that overrides a virtual function must also use the virtual keyword on it.
  • Incorrect. It can be useful to do so as a reminder, but it is not required.
  • If you expect a class will be inherited from and child classes might need to override a particular function, you should probably make it virtual.

Subsection 19.7.3 Virtual Functions - Syntax Notes

When you separate the declaration of a virtual function from its definition, you only add virtual to the declaration inside the class. The implementation outside can not have the virtual keyword.
For example:
Listing 19.7.2.
class Person {
    virtual void introduce() const; // In class declaration gets virtual
};

void Person::introduce() const { 
    // Outside of class definition of introduce does not get virtual
}
A little confusing. But donโ€™t worry too much, if you by accident add virtual to the definition the compiler will produce an error to let you know. You just need to know that the message error: โ€˜virtualโ€™ outside class declaration is trying to tell you that you made that mistake.
You also can add the override keyword at the end of the declaration of a virtual function in a derived class:
class Student : public Person {
    virtual void introduce() const override {...} // Example of using override
};
Doing so will not change how the code works and is not required. But it is a nice reminder of what is going on. It also tells the compiler to make sure that the function is actually overriding a base class function. This can prevent bugs such as declaring void intorduce() (spelling error!) in the child class and then wondering why it is never used. Doing so would make a new function in the child class instead of overriding a function from the parent class. With the override keyword, the compiler would mark inroduce() as an error because it does not successfully override any existing functions.
You have attempted of activities on this page.