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).

3 Comments


  1. Interesting post, simple and clear, thanks for sharing.
    Just a small typo in the last code snippet, some html still there:

    Reply
    • Marco Foco

      Thank you! 🙂 I have also fixed last code snippet, thanks for noticing!

      Reply

  2. 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

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *