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 usefinal
then is if you write:virtual void foo() final;
in the base class. You make a functionvirtual
but at the same timefinal
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. Onbar()
it makes sense but onfoo()
it is unnecessary, becausefinal
requiresvirtual
. Iffoo()
’s signature changes, the compiler will complain anyway thatfoo()
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 usedoverride
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 useprotected
. 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
friend
s.
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
. But8 > 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:
-
Don’t declare empty base classes
final
. -
Also consider removing it from non-
virtual
classes for consistency. The fact that they cannot be used polymorphically is already shown by the lack ofvirtual
functions, sopublic
inheritance should not be done anyway. And as a class author there is rarely a use to preventprivate
/protected
inheritance, so thefinal
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 blog post was written for my old blog design and ported over. If there are any issues, please let me know.