foonathan::blog()

Thoughts from a C++ library developer.

You (probably) don't want `final` classes

C++11 introduced the final “keyword”. It can be used to mark member functions and classes as final, meaning that they cannot be overridden in derived classes/be base classes.

In this post I’ll take a closer look and explain why I consider the use of final classes problematic in some cases.

final member functions

A final member function is a member function that cannot be overridden. Of course that only makes sense for virtual member functions because non-virtual functions cannot be overridden by definition. And that means that the final keyword on member functions only makes sense in a derived class.

Because the function must be virtual so the only way you can use final then is if you write: virtual void foo() final; in the base class. You make a function virtual but at the same time final so it cannot be overridden. I hope we all agree why this doesn’t make sense. Please don’t write such code.

So consider an example:

class base
{
public:
    virtual void foo();	
    virtual void bar();
};

class derived : public base
{
public:
    // foo() is now finished; there is now possible way it could be improved
    void foo() override final;
    
    // bar() isn't finished; it could be improved
    void bar() override;
};

Notice the abstract example instead of something “real”. That is because I couldn’t come up with a “real” example. Not even one with allocators.

So we have a base class base with two virtual functions foo() and bar(). Then we create a derived class derived that overrides both functions. But when writing the implementation of foo() we don’t want that a class second_derived can override foo() again. It should only override bar(), foo() is perfect the way it is. So we use final on foo().

I’ve also used override on both functions. On bar() it makes sense but on foo() it is unnecessary, because final requires virtual. If foo()’s signature changes, the compiler will complain anyway that foo() isn’t virtual. But as the saying goes “a good programmer is someone who looks both ways before crossing a one way street”, I’ve used override anyway. I also find it clearer.

So that is final on member functions. I have no problem with it. I find it a little bit unnecessary and cannot see a real use case but otherwise it is alright.

And others may need it.

final on classes

But suppose you realize that bar() should also be final. In fact it doesn’t make sense to derive from derived at all!

For that you can use final on classes:

class base
{
    // as before
};

class derived final
: public base
{
    // as before
    // final on member functions no unnecessary
};

Now it is an error if you try to inherit from derived all together.

There are actually two different cases where you might want to use it: on a derived class to prevent further derivation or on a class that is neither base or derived class to prevent it being used in an inheritance hierarchy at all. In the latter case you have a function without any virtual functions so you want to prevent that someone tries to use polymorphically. In both cases final can be used to prevent inheritance.

But if you do that on certain types I hate you and my code will punish you. For that let’s recap the use for inheritance.

Inheritance applications

The most prominent feature of inheritance - at least for the “OOP people” - is to enable polymorphism. Then the base class has (pure) virtual functions and a (pure) virtual destructor. Derived classes inherit public from it.

So far, so Java.

But there are other cases of inheritance apart from public inheritance. You can also use private inheritance.

And protected but I can’t imagine a case where I want to use protected. Yes, I’ve read Effective C++ or where it is mentioned but I can’t imagine a case where you want to have an “is-a” relationship for derived classes.

While public inheritance models “is-a” relationship (or: is supposed to model), private inheritance models - well - nothing really. It is an implementation detail, it is invisible to outsiders.

Except friends.

So where should you use it then?

Anywhere where you need inheritance but the base class doesn’t have a virtual destructor and/or you don’t want to have polymorphism. One example are classes with a protected destructor that are intended as helper classes but must be derived. Normally composition is clearly preferred but sometimes there are valid use cases for that.

One of them is policy based design.

Policy base design

Policy based design, as e.g. described in Alexandrescu’s Modern C++ Design book, is the technique of extracting a class into different policies, each of them taking care of a certain aspect that should be customized by the user.

For example, suppose you have a container class. The container needs to allocate dynamic memory and it would be convenient if that could be customized. This can be done via policy-based design. The container class takes an additional template parameter, the Allocator which is a user-defined type - a policy - that must provide a certain set of functions. Instead of manually calling new and delete inside the container, it calls some functions of the given policy type. A user can then simply create a new allocator type to customize that behavior.

Policy-based design is great, with many applications. But there is a fundamental question: how to store the policy? You can either go the normal way and have it as a member or you can inherit from it. The latter has a benefit compared to the former way because of three letters and a common trait of policy classes.

EBO

Policy classes are only there to provide some functions that can be called. Often a policy class doesn’t actually store any data members, they are empty.

What is the size of an empty type?

It isn’t 0 as you might think, it is 1.

Because of some stuff with addressing and different objects I don’t want to bother you with.

So if you have an empty (policy) class as member you still have to pay some bytes for it! With regard to alignment this can be a huge waste of space.

Huge being 8. But 8 > 0.

You can fortunately work around that by inheriting from an empty type. Then the compiler is allowed to enable the Empty Base Optimization.

Except in some rare cases where you have the same type also as first member. But I don’t want to bother you with that either.

Empty base classes (can) have 0 size. But only as base class. So policy classes should be stored as (private) bases.

This has a consequence with regard to final: if you have an empty class that is final you cannot use the EBO.

And it also requires complicated code in the derived class because it needs a fallback. For that reason in my code I require that policy classes are not final. But this may not be possible everywhere.

This can be avoided if the class wasn’t declared final in the first place! So please follow the following guidelines it makes the life of (generic) library authors easier and your life better:

  1. Don’t declare empty base classes final.

  2. Also consider removing it from non-virtual classes for consistency. The fact that they cannot be used polymorphically is already shown by the lack of virtual functions, so public inheritance should not be done anyway. And as a class author there is rarely a use to prevent private/protected inheritance, so the final can be omitted.

With “rarely” here I mean “I cannot think of any, but want a fallback for extra smart people”.

The only possible use of final is on a polymorphic derived class that shouldn’t be modified further and you are too lazy to put final on each member function. But as said before: I cannot think of a reason for that either. So personally I never use final.

Update: Jon Kalb has provided a good reasoning for the use of final on classes in a polymorphic hierarchy in the comments below. As Scott Meyers Guideline 33 of MEC++ states “You should make non-leaf classes abstract”. And final classes are necessarily leaf classes. So the keyword gives class writers the power to enforce this idiom by preventing inheritance for that.

Conclusion

The final keyword can be used to a) prevent further overriding of member functions and b) to prevent inheritance from classes. Use a) is alright but use b) has some problems because it prohibits the use of the EBO in generic code.

I thus advocate that you should only rarely use b) and only on classes with virtual functions.

This post was made possible by my Patreon supporters. If you'd like to support me as well, please head over to my Patreon and do so! One dollar per month can make all the difference.