The final Specifier
/ 3 min read
Table of Contents
The final specifier in C++11 lets you explicitly prevent inheritance or virtual function overriding. It’s great for enforcing design decisions and catching mistakes.
What final Actually Does
final can be used in two ways:
- On classes: Prevents the class from being inherited
- On virtual functions: Prevents the function from being overridden further
class Base {public: virtual void func() = 0;};
class Derived final : public Base { // Cannot be inheritedpublic: void func() override final; // Cannot be overridden};
// class Error : public Derived {}; // Compilation errorWhy This Is Useful
Before final, there was no way to explicitly prevent inheritance. You had to rely on documentation or complex workarounds:
// The old way - hacky and unclearclass NonInheritable { friend class ActualClass;private: NonInheritable() {}};
class ActualClass : virtual NonInheritable {public: ActualClass() {}};With final, intent is clear and enforced by the compiler.
When I Actually Use final
Most of the time, I use final in these situations:
- Leaf classes: When a class represents a complete, final implementation:
class FileLogger final : public Logger {public: void log(const std::string& message) override { file_ << message << std::endl; }private: std::ofstream file_;};- Performance-critical classes: Where I want to enable compiler optimizations:
class Vector3D final { float x_, y_, z_;public: float dot(const Vector3D& other) const { return x_ * other.x_ + y_ * other.y_ + z_ * other.z_; } // Compiler can optimize knowing this won't be inherited};- Preventing further overriding: When a virtual function implementation is complete:
class Shape {public: virtual double area() const = 0; virtual void draw() const = 0;};
class Rectangle : public Shape {public: double area() const override { return width_ * height_; } void draw() const override final { // Complete implementation // Drawing logic that shouldn't be changed }private: double width_, height_;};- Security or stability: When inheritance could break invariants:
class CryptoHash final { // Inheritance could compromise securitypublic: std::string hash(const std::string& input) const { // Secure hashing implementation }};The Design Benefits
Using final makes design intent explicit:
// Clear hierarchy designclass Animal {public: virtual void make_sound() const = 0;};
class Mammal : public Animal {public: virtual void breathe() const { std::cout << "Breathing air" << std::endl; }};
class Dog final : public Mammal { // Dogs are concrete animalspublic: void make_sound() const override final { std::cout << "Woof!" << std::endl; }};Performance Implications
final classes enable compiler optimizations:
class OptimizedClass final {public: void hot_function() const { // Compiler can inline this more aggressively // because it knows no derived classes exist }};The compiler knows there are no derived classes, so it can eliminate virtual function call overhead in some cases.
Common Patterns
I often see these patterns:
- Value types: Mathematical or utility classes:
class Point final { double x_, y_;public: Point(double x, double y) : x_(x), y_(y) {} // Mathematical operations...};- RAII wrappers: Resource management classes:
class FileHandle final { FILE* file_;public: explicit FileHandle(const std::string& filename) : file_(fopen(filename.c_str(), "r")) {}
~FileHandle() { if (file_) fclose(file_); }
// No copying or inheritance FileHandle(const FileHandle&) = delete; FileHandle& operator=(const FileHandle&) = delete;};The Rule I Follow
Use final when:
- The class represents a complete, concrete implementation
- Inheritance would break the class’s design or invariants
- Performance is critical and you want to enable optimizations
- You want to make design intent explicit
Don’t use final when:
- The class might reasonably be extended in the future
- You’re building a library where users might want to inherit
- The benefits aren’t clear
final is about being explicit with design decisions. It documents intent and lets the compiler help enforce it.