C++ Virtual Methods: An Overview And Some Things You Might Not Know

Callum Thompson
Level Up Coding
Published in
4 min readMay 3, 2022

--

A friendly-to-read explanation of C++ virtual methods with examples.

What are Virtual Methods?

When a method is decorated with the virtual keyword, it allows for its behaviour to be overridden in a derived object.

Let’s look at the above code.

DA overrides the behaviour of speak from Base. When an instance of DA is created, it calls this new behaviour instead of what is implemented in Base. This is shown on line 34.

To override a method, the base method should be marked as virtual and the derived method should have the same:

  • Name
  • Parameter type list (different names are okay, but not recommended)
  • Return type (or covariant)
  • cv qualfiers
  • ref qualifiers

If a derived class is handled using the base class, a call to an overridden virtual method would call the behaviour implemented in the derived class. This is shown on lines 37 and 40.

Calls to any virtual function where qualified name lookup is not used is known as a virtual call. The behaviour in the overrode method is executed. Even line 33 is a virtual call.

The virtual call is suppressed if the method is called using qualified name lookup. This is shown in lines 42–44. This is known as a direct call.

The Override Identifier

Let’s look at line 18, where the behaviour of speak from Base is overridden.

To override a virtual method, you don’t need to explicitly use the override identifier. However, it can be helpful to use it for these reasons:

  • It makes it obvious to whoever is reading the code that a virtual method is being overridden. There is no guess work.
  • The compiler knows if a method can be overridden or not, but by using the override identifier, the compiler can check that you are not trying to override something that isn’t overrideable.

Override is an Identifier, Not a Keyword

override has a special meaning when it is used after a member method is declared. However, aside from this, it is not reserved like a keyword is. For example, the below code is completely valid:

In saying this, override should still be treated as reserved, to avoid confusion for the reader.

Overriding a Private Virtual Member

The base member method does not need to be accessible to be overridden. It can be declared as private, or the base class can be inherited using private inheritance.

In the above code, even though yawn in Base is private, it is still able to be overridden by DA.

If we handle an instance of DA with the base class, we are no longer able to call yawn, as it is no longer accessible. Instead, it can be called through a public method named do_yawn.

In additional to this, even when using qualified name lookup, the virtual call to yawn is not suppressed. See lines 57–59, do_yawn calls the DA implementation of yawn, not the Base implementation. The virtual call is only suppressed to the first method called, after that any overrode behaviour is still used. The call tree of lines 57–59 are Base::do_yawn → DA::yawn.

There is lots of conflicting information about marking virtual methods as private, and I haven’t looked into this enough to decide what I think is best. Maybe I’ll write an article on the best practices around private virtual methods in the future.

The Final Identifier

The final identifier (not a reserved keyword, like override) specifies that a virtual method cannot be overridden in a derived class, or that a class cannot be inherited.

In the above code, we have made Base::speak final. It cannot be overridden. After doing this, an error will be raised in DA where speak is currently overrode, saying that Base::speak cannot be overridden as it is marked as final.

Final Overrider

A virtual member method is the final overrider if nothing else overrides that method. The final overrider is the virtual method, which is executed through a virtual call.

Ambiguity

Ambiguity arises when a method has more than one final overrider.

In the above code, there is no way to know which method d.speak() should call, as there are inherited implementations in both B and C. Such ambiguity caused by multiple inheritance is referred to as the diamond problem.

Unintentionally Hiding a Virtual Method

If a method attempts to override a method from the base class with a different parameter type list, it does not override that method. Instead, it defines a separate new method, and hides the method inherited from the base class.

DA is unable to override Base::jump and instead hides it and defines a separate jump method. For DA to access Base::jump it must call it using qualified name lookup (line 40).

Still More to Discuss

This is by no means an exhaustive overlook of virtual methods in C++. In future articles, I aim to discuss:

  • Virtual destructors
  • Virtual methods during construction and destruction
  • Covariant return types
  • Pure virtual methods

I’ll link the above here once complete.

References

The aim of this article is to provide a more friendly overlook of C++ virtual methods outlined in the below documentation.

https://en.cppreference.com/w/cpp/language/final
https://en.cppreference.com/w/cpp/language/override
https://en.cppreference.com/w/cpp/language/virtual

--

--