I’m preparing some slides to explain how to write better code with C++11/14, and I wanted to start from the last thing I wrote about here, the use of keywords final
and override
.
The scope of my previous post, albeit a bit cryptic, was to show how those keywords are context-sensitive, so you can also use final
and override
as type names, as namespaces, as function names, as variables and so on, without generating any kind of error. I’m not suggesting you to use this keywords in other contexts, but some legacy code might have used them, and that code will still be legal C++11.
Why final
and override
are so important? To improve code readability and to enforce compile time correctness.
final
can be used in two contexts, to disable inheritance from this class and to disallow overriding a virtual
method.
final for classes
The first case is very simple:
class A final {}; class B : public A {};
This code will result in an error, because we are trying to use A
as a base class for B
, despite having declared A
as final
:
main.cpp:2:18: error: base 'A' is marked 'final' class B : public A {}; ^ main.cpp:1:7: note: 'A' declared here class A final {}; ^ ~~~~~
There was a pre-C++11 solution, which is was way less readable, and the relative error message was not so clear. In order to make a class non inheritable, you had to create a helper class with a private
constructor (or, even better, destructor), and make it friend of our class A
:
class final_helper_for_A { friend class A; private: ~final_helper_for_A() {} }; class A : virtual public final_helper_for_A {}; class B : public A {};
But this code alone won’t generate any error.
In order to have the compiler complaining about our code, we have to create an instance of B somewhere, and that’s where the error is signalled:
main.cpp:11:3: error: call to implicitly-deleted default constructor of 'B' B b; ^ main.cpp:7:11: note: default constructor of 'B' is implicitly deleted because base class 'final_helper_for_A' has an inaccessible destructor class A : virtual public final_helper_for_A {}; ^
In this sense, the use of final
keyword for classes helps i three ways:
- increasing readability of code, by allowing a more compact syntax
- increasing readability of error messages, by generating an explicit errors
- enforcing compile time correctness, by placing the error in the declaration (if, for example, you’re creating a library, the pre-C++11 version will result in an error when tests are compiled, not when building the library)
final for methods
Another use for final
is to inhibit a derived class to override a specific virtual
method.
class A { public: virtual void a(); }; class B : public A { public: void a() final; }; class C : public B { void a(); };
Note that the example would have been easier if we declared the function a
both virtual
and final
directly in class A
, but it would’ve been pointless to declare it final in the first place (or, at least, I can’t see a point for that – if you see it, please add a comment below).
In this case, overriding the method in class A
generates an error:
main.cpp:12:7: error: declaration of 'a' overrides a 'final' function void a(); ^ main.cpp:8:7: note: overridden virtual function is here void a() final; ^
There was no way to obtain such an error in pre-C++11, this makes this use of final
for methods much more powerful than for classes, even if it just enforces compile-time correctness. But these are just corner case, it doesn’t happen that often to have such a situation where you want to forbid overriding.
It happens much more often to actually overriding methods, and, until C++11, you had nothing to help you discovering whether your override was correct or not.
override for methods
This little keyword actually saved me a huge amount of time in debugging during a recent port of a rather big application from one version of a commercial library to a newer one.
One of our base classes (a class belonging to the library, so it wasn’t under our control) had a method whose signature was changed in the newer version, and that method was representing a “data received” event. For this reason, we were supposed to override the method, in order to receive the events.
The code went like this:
Version 1:
namespace lib { class A { public: virtual void data_received() { /* default behaviour here */ } // some other code here was calling data_received }; }
And our application was something like this:
namespace app { class B : public lib::A { public: void data_received() { std::cout << "I received some data!" << std::endl; } }; }
So, our application was writing a string every time the library signalled the presence of new data.
Luckily we decided to upgrade our codebase to use some C++11 features, such as override
, before upgrading to the new library, but I wanted to show you what would have happened if we didn’t.
Version 2 of the library went like this:
namespace lib { class A { public: virtual void data_received(int amount) { /* default behaviour here */ } // some other code here was calling data_received }; }
See what happens? Now user code is no longer overriding the virtual
method data_received()
because there’s no such method! It’s rather overloading the data_received(int)
method with another method taking no parameters, and hiding the base method. A completely different beast!
But no compiler would have produced any error or warning, because overloading and hiding are perfectly legal things to do.
As I said, luckily we decided to change our application code before upgrading, following the suggestions provided by Jetbrain’s Resharper C++ Visual Studio plugin:
namespace app { class B : public lib::A { public: void data_received() override { std::cout << "I received some data!" << std::endl; } }; }
This little change alone resulted in a pretty clear error message after upgrading the library to Version 2:
main.cpp:15:30: error: non-virtual member function marked 'override' hides virtual member function void data_received() override { std::cout << "I received some data!" << std::endl; } ^ library.h:5:22: note: hidden overloaded virtual function 'lib::A::data_received' declared here: different number of parameters (1 vs 0) virtual void data_received(int amount) { /* default behaviour here */ } ^
Obviously we filed a defect report to the library provider, because he decided to change a public virtual
function without notice.
So, now you know why I sponsor the usage of final
and override
so much!
NOTE: Despite mentioning Visual Studio in the post, the error messages comes from the latest (at the time of writing) clang compiler provided by XCode on MacOSX, which is Apple LLVM version 7.0.2 (clang-700.1.81)
.
#
Interesting post, simple and clear, thanks for sharing.
Just a small typo in the last code snippet, some html still there:
#
Thank you! :) I have also fixed last code snippet, thanks for noticing!
#
Please note, final and override are not keywords, they are what is referred to as “identifiers with special meaning”. If they were keywords you would not be able to use override and final as identifiers. This works because they are being used in a place where the grammar does not allow identifiers. For more details see my Stackoverflow question: http://stackoverflow.com/q/30404388/1708801
#
Sorry, I didn’t notice this comment before.
You’re correct, in the standard the title of the table listing final and override says “identifiers with special meaning”.
Nonetheless they are often referred to as “contextual keywords” or “context-sensitive keywords” in papers and common talking, including in the answer to your question.
Sorry for the confusion.
#
Just a small remark here. I think in past developers take care more about warnings as they compilers where less strict as today. And what I can see it is:
hides overloaded virtual function [-Woverloaded-virtual] warning.
Actually if they really change interface, then your non override function hide virtual function and make it invisible for class B. The next error which could be produced is unknown function call during polymorphism.
The change which comes with override is strong relation. Thnks, for post!