Jonathan Boccara's blog

Virtual, final and override in C++

Published February 21, 2020 - 0 Comments

C++11 added two keywords that allow to better express your intentions with what you want to do with virtual functions: override and final. They allow to express your intentions both to fellow humans reading your code as well as to the compiler.

However, as we will see, the intention of override is super useful, but the intention of final… is harder to understand.

Both apply to virtual functions, which are the member functions of a base class that can be overridden by the classes that derive (inherit) from it.

override: a useful feature to prevent bugs

override is a feature to use without moderation! Every time you define a method in the derived class that overrides a virtual method in the base class, you should tag it override:

class Base
{
public:
    virtual void f()
    {
        std::cout << "Base class default behaviour\n";
    }
};

class Derived : public Base
{
public:
    void f() override
    {
        std::cout << "Derived class overridden behaviour\n";
    }
};

This way you show that your intention for the derived class is to override the behaviour of f in the base class.

Note that in term of execution, the above code is equivalent to this one:

class Derived : public Base
{
public:
    void f()
    {
        std::cout << "Derived class overridden behaviour\n";
    }
};

Even without writing override, f overrides the behaviour of its counterpart in the base class (as long as the f of the base class is virtual and has the same prototype). So override is really about expressing your intentions.

Expressing your intentions to the compiler

One of the persons (kind of) you can express your intentions to is the compiler. If you tag a member function override, the compiler will make sure that the member function exists in the base class, and prevent the program from compiling otherwise.

This is particularly useful for const member functions, because they are error-prone in the context of overriding. For instance, in the above code, if your code in Derived happens not to modify the object, you could think of tagging the method const, which is in general a good practice:

class Base
{
public:
    virtual void f()
    {
        std::cout << "Base class default behaviour\n";
    }
};

class Derived : public Base
{
public:
    void f() const override
    {
        std::cout << "Derived class overridden behaviour\n";
    }
};

But void f() and void f() const are two different prototypes, and the derived class no longer overrides the method of the base class. Indeed, the overriding of member functions in C++ is based on prototype (void f()) and not just on the name of the method (f).

If you think that a member function overrides another one and in fact it doesn’t, it can lead you into maddening debugging sessions before you understand what is going on.

override prevents the above code from compiling in the first place.

Note that this is different from using virtual in the derived class:

class Base
{
public:
    virtual void f()
    {
        std::cout << "Base class default behaviour\n";
    }
};

class Derived : public Base
{
public:
    virtual void f() const // doesn't check anything!
    {
        std::cout << "Derived class overridden behaviour\n";
    }
};

Adding virtual here creates a new virtual function that could be overridden in the derived classes of Derived itself. It doesn’t check that f in Derived overrides f in Base. The code compiles without a problem and lets the bug slip in.

override makes the compilation fails when there is a difference in const like in the above example, and also does it for more visible differences in prototypes, such as adding or removing parameters, or renaming the member function or removing it altogether.

override makes sure that a base class has an identical prototype in one of its virtual functions.

Maybe you have access to override without knowing it

override is a C++11 feature. If you’re stuck with C++98 or C++03, you don’t have access to it in theory.

But if you’re compiling with several compilers, maybe one of them has override? If it is the case, you could define a macro OVERRIDE that resolves to override if you’re compiling with this one, and to an empty string for the other compilers.

You can then define your functions OVERRIDE, and if they don’t override a virtual function from the base class, this bug will show on the build with the compiler that has override, and you will be able to fix your code.

override is such a great help that it’s worth checking if by any chance your compiler(s) implement(s) it.

final: a useful feature?

final came along with override in the C++11 standard. Like override, you can tag your virtual member function with final.

class Base
{
public:
    virtual void f()
    {
        std::cout << "Base class default behaviour\n";
    }
};

class Derived : public Base
{
public:
    void f() final
    {
        std::cout << "Derived class overridden behaviour\n";
    }
};

This prevents any derived class of Derived to override the member function f.

So far I have never used final member functions, and have never seen it used in code.

I guess they can be useful for the implementer of Derived, to make sure that they know what is actually executed when the member function is called. This can help modify the code of the method later, and have control on how that impacts the program. Indeed, without final a derived class of Derived could have overridden the method’s code.

If you use final member functions, please leave a comment to explain why this is useful.

EDIT: final can give the compiler an opportunity to improve performance by devirtualization. See this detailed article by Sy Brand for more about this.

final classes

The final keyword applies to member function, but unlike override, it also applies to types:

class X final
{
    // ...
};

This prevent the type to be inherited from.

Here again, it is difficult to understand the purpose of such a feature. Andrzej Krzemieński wrote a blog post discussing why we’d make classes final, and the outcome is that there are indeed very few cases where this could be useful, if any. The blog post has an interesting discussion, that also has pretty much the same outcome.

I’m no Java expert, but I gather that the point of final classes in Java is to guarantee that objects of such classes are immutable.

This is pure speculation, but maybe this can apply to C++ too: if a class is final and all of its methods are const, then its interface says that no objects of this class can be modified. Without final, you could have a derived class that adds new member functions that modify the object.

As a result, if you’re passed a reference (or a const reference) to an object of a final class, you have the guarantee that it won’t be modified by someone else, so you can safely use it across threads, reason about it, or whatever benefits of immutable objects.

Check out this post from Andrzej Krzemieński’s blog and its discussion for other suggestions on where final classes could be useful.

A final word

In conclusion, override is super useful to express your intentions in code, and easily prevent some bugs you really don’t want to investigate. You should use it as much as possible.

The purpose of final is harder to understand. I’ve never used final on classes or on member functions, nor seen code using it.

Have you used final in C++, on classes or on methods? How was it useful?

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin