C++11 final Keyword
Table of Contents
- What is the final Keyword?
- How Programmers Achieved This Before C++11
- How final Keyword Improved the Code
- When to Use final?
- Best Practices and Guidelines
What is the final Keyword?
The final keyword, introduced in C++11, is used to restrict inheritance and method overriding. It can be applied in two contexts:
- Final Classes - Prevents a class from being inherited
- Final Methods - Prevents a virtual method from being overridden in derived classes
Preventing Class Inheritance
When a class is marked as final, no other class can inherit from it.
Syntax:
class ClassName final {
// Class definition
};
Example:
#include <iostream>
using namespace std;
// This class cannot be inherited
class FinalClass final {
public:
void display() {
cout << "This is a final class" << endl;
}
};
// Attempting to inherit from FinalClass
class DerivedClass : public FinalClass { // ERROR: Cannot inherit from final class
public:
void show() {
cout << "Derived class" << endl;
}
};
int main() {
FinalClass obj;
obj.display();
return 0;
}
Compiler Error:
error: cannot derive from 'final' base 'FinalClass' in derived type 'DerivedClass'
Preventing Method Override
When a virtual method is marked as final, derived classes cannot override it.
Syntax:
virtual return_type methodName() final {
// Method implementation
}
Example:
#include <iostream>
using namespace std;
class Base {
public:
virtual void display() {
cout << "Base display" << endl;
}
// This method cannot be overridden
virtual void show() final {
cout << "Base show - cannot be overridden" << endl;
}
};
class Derived : public Base {
public:
// This is allowed
void display() override {
cout << "Derived display" << endl;
}
// This will cause a compilation error
void show() override { // ERROR: Cannot override final method
cout << "Derived show" << endl;
}
};
int main() {
Derived obj;
obj.display();
obj.show();
return 0;
}
Compiler Error:
error: virtual function 'virtual void Derived::show()' overrides final function
How Programmers Achieved This Before C++11
Before C++11, there was no direct language support for preventing inheritance or method overriding. Programmers used various workarounds, all with significant limitations.
Private/Protected Constructor Approach
One common technique was to make constructors private or protected, preventing direct instantiation of derived classes.
#include <iostream>
using namespace std;
class NonInheritableClass {
private:
NonInheritableClass() { // Private constructor
cout << "NonInheritableClass created" << endl;
}
public:
// Factory method for creating instances
static NonInheritableClass* create() {
return new NonInheritableClass();
}
void display() {
cout << "Display method" << endl;
}
};
// Attempting to inherit
class DerivedClass : public NonInheritableClass {
public:
DerivedClass() { // ERROR: Cannot access private constructor
cout << "Derived class" << endl;
}
};
int main() {
// Cannot create object directly
// NonInheritableClass obj; // ERROR
// Must use factory method
NonInheritableClass* obj = NonInheritableClass::create();
obj->display();
delete obj;
return 0;
}
Friend Class Approach
Another technique combined private constructors with friend classes for controlled creation.
#include <iostream>
using namespace std;
class NonInheritableClass;
// Helper class that can create NonInheritableClass
class Creator {
public:
static NonInheritableClass* create();
};
class NonInheritableClass {
private:
NonInheritableClass() {
cout << "Created via friend" << endl;
}
friend class Creator; // Only Creator can access private constructor
public:
void display() {
cout << "Display method" << endl;
}
};
NonInheritableClass* Creator::create() {
return new NonInheritableClass();
}
int main() {
NonInheritableClass* obj = Creator::create();
obj->display();
delete obj;
return 0;
}
Problems with Pre-C++11 Approaches
These workarounds had several significant issues:
1. No Direct Method Override Prevention
class Base {
public:
virtual void criticalMethod() {
// Important logic that shouldn't be changed
}
};
class Derived : public Base {
public:
// No way to prevent this override before C++11
void criticalMethod() override {
// Oops! Accidentally overridden
}
};
2. Complex and Error-Prone Code
// Required complex boilerplate code
class SafeClass {
private:
SafeClass() {}
static SafeClass* instance;
public:
static SafeClass* getInstance() {
if (!instance) {
instance = new SafeClass();
}
return instance;
}
// Lots of additional code needed...
};
SafeClass* SafeClass::instance = nullptr;
3. Unclear Intent
// Why is the constructor private? To prevent inheritance or for Singleton pattern?
class MyClass {
private:
MyClass() {} // Intent is not clear
public:
static MyClass* create() {
return new MyClass();
}
};
4. Memory Management Burden
// Forced to use pointers and factory methods
MyClass* obj = MyClass::create();
obj->doSomething();
delete obj; // Must remember to delete
// Could not simply do:
// MyClass obj; // Direct instantiation not possible
5. Incomplete Prevention
class Base {
private:
Base() {}
public:
static Base create() {
return Base();
}
};
// This still compiles in some cases!
class Derived : public Base {
// Can still inherit even with private constructor
};
How final Keyword Improved the Code
The final keyword provides a clean, explicit, and reliable solution that addresses all the problems of previous approaches.
Clear Intent
The final keyword makes the programmer’s intent immediately obvious.
Before C++11:
class Configuration {
private:
Configuration() {} // Why private? Not immediately clear
public:
static Configuration* getInstance();
void setOption(string key, string value);
};
With final:
class Configuration final {
public:
Configuration() {} // Clear: this class cannot be inherited
void setOption(string key, string value);
};
Compile-Time Enforcement
The compiler enforces the restriction, catching errors early.
class SecurityManager final {
public:
void authenticate(string username, string password) {
// Critical security logic
}
};
// Compiler immediately catches this error
class CustomSecurityManager : public SecurityManager { // COMPILE ERROR
// Cannot compromise security by inheriting
};
Better Error Messages
Clear, understandable compiler errors help developers fix issues quickly.
Example:
class ImmutableString final {
string data;
public:
ImmutableString(string s) : data(s) {}
string get() const { return data; }
};
class MutableString : public ImmutableString { // ERROR
public:
void set(string s) { /* ... */ }
};
Compiler Error:
error: cannot derive from 'final' base 'ImmutableString'
This is much clearer than cryptic errors about private constructors!
Performance Optimizations
The compiler can make optimization decisions knowing that methods won’t be overridden.
class FastMath {
public:
virtual int add(int a, int b) final {
return a + b;
}
virtual int multiply(int a, int b) final {
return a * b;
}
};
// Compiler knows these methods are final and can:
// - Inline them more aggressively
// - Skip virtual table lookups
// - Apply devirtualization optimizations
Comparison Example:
#include <iostream>
#include <chrono>
using namespace std;
class NonFinalClass {
public:
virtual int compute(int x) {
return x * x;
}
};
class FinalClass {
public:
virtual int compute(int x) final {
return x * x;
}
};
int main() {
NonFinalClass nfc;
FinalClass fc;
const int iterations = 100000000;
// Non-final method call
auto start = chrono::high_resolution_clock::now();
int sum1 = 0;
for(int i = 0; i < iterations; i++) {
sum1 += nfc.compute(i);
}
auto end = chrono::high_resolution_clock::now();
auto duration1 = chrono::duration_cast<chrono::milliseconds>(end - start);
// Final method call (potentially optimized)
start = chrono::high_resolution_clock::now();
int sum2 = 0;
for(int i = 0; i < iterations; i++) {
sum2 += fc.compute(i);
}
end = chrono::high_resolution_clock::now();
auto duration2 = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "Non-final time: " << duration1.count() << "ms" << endl;
cout << "Final time: " << duration2.count() << "ms" << endl;
return 0;
}
Simplified Code Structure
No need for complex workarounds or boilerplate code.
Before C++11 (50+ lines):
class Singleton {
private:
static Singleton* instance;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
void doWork() {
cout << "Working..." << endl;
}
};
Singleton* Singleton::instance = nullptr;
// Usage requires pointers
Singleton* obj = Singleton::getInstance();
obj->doWork();
With final (10 lines):
class Singleton final {
private:
Singleton() {}
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void doWork() {
cout << "Working..." << endl;
}
};
// Usage is cleaner
Singleton::getInstance().doWork();
When to Use final?
Use Cases for final Classes
1. Utility Classes with Static Methods
Classes that only contain static helper functions should be final.
class MathUtils final {
public:
static double sqrt(double x) {
// Implementation
return 0.0;
}
static double pow(double base, double exp) {
// Implementation
return 0.0;
}
// No need for inheritance - just utility functions
};
2. Value Objects / Data Transfer Objects (DTOs)
Simple data containers that represent immutable values.
class Point final {
private:
int x, y;
public:
Point(int x, int y) : x(x), y(y) {}
int getX() const { return x; }
int getY() const { return y; }
// No need to extend - it's just a point
};
3. Implementation Classes (Not Interfaces)
Concrete implementations that should not be further specialized.
class HttpClient final {
public:
void sendRequest(string url) {
// Concrete implementation
cout << "Sending HTTP request to " << url << endl;
}
string receiveResponse() {
// Concrete implementation
return "Response data";
}
};
4. Security-Critical Classes
Classes where inheritance could compromise security or correctness.
class PasswordHasher final {
public:
string hash(string password) {
// Critical hashing algorithm
// Must not be altered by inheritance
return "hashed_password";
}
bool verify(string password, string hash) {
// Critical verification logic
return true;
}
};
Use Cases for final Methods
1. Template Method Pattern - Fixed Steps
When certain steps in an algorithm must never change.
class DataProcessor {
public:
// Template method defines the algorithm
void process() {
readData();
validateData(); // This step is fixed
transformData(); // This can be customized
writeData(); // This step is fixed
}
protected:
virtual void readData() {
cout << "Reading data..." << endl;
}
// This validation must always happen exactly this way
virtual void validateData() final {
cout << "Performing mandatory validation..." << endl;
// Critical validation logic that must not be changed
}
virtual void transformData() = 0; // Subclasses must implement
// Writing must follow specific protocol
virtual void writeData() final {
cout << "Writing data with integrity checks..." << endl;
// Must not be altered
}
};
class CSVProcessor : public DataProcessor {
protected:
void transformData() override {
cout << "Converting to CSV format..." << endl;
}
// Cannot override validateData() or writeData() - they are final
};
2. Performance-Critical Methods
Methods that are optimized and should not be overridden.
class GraphicsRenderer {
public:
// Highly optimized rendering code
virtual void render() final {
// Assembly-optimized or GPU-accelerated code
// Must not be overridden to maintain performance
cout << "Optimized rendering..." << endl;
}
virtual void setColor(int r, int g, int b) {
// Can be overridden
}
};
3. Preventing Accidental Override
Methods that work correctly and should not be accidentally broken.
class BankAccount {
protected:
double balance;
public:
BankAccount(double initial) : balance(initial) {}
virtual void deposit(double amount) {
if(amount > 0) {
balance += amount;
}
}
// Critical business logic - must not be changed
virtual bool withdraw(double amount) final {
if(amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
double getBalance() const {
return balance;
}
};
class SavingsAccount : public BankAccount {
public:
SavingsAccount(double initial) : BankAccount(initial) {}
// Can add interest calculation
void addInterest(double rate) {
deposit(balance * rate);
}
// Cannot override withdraw() - protected by final
};
4. Ensuring Contract Compliance
When a method implements a critical contract that must be maintained.
class Observable {
private:
vector<Observer*> observers;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
// Notification must always work this way
virtual void notify() final {
for(auto obs : observers) {
obs->update(this);
}
}
virtual void setState(int state) {
// Can be overridden
}
};
When NOT to Use final
1. Library/Framework Base Classes
Classes designed to be extended by users.
// DON'T do this
class Widget final { // BAD - users might want to extend
public:
virtual void render();
};
// DO this instead
class Widget {
public:
virtual void render();
virtual ~Widget() {}
};
2. When Extensibility is a Feature
Classes that are meant to be customized.
// DON'T do this
class Plugin final { // BAD - plugins need to be extended
public:
virtual void execute();
};
// DO this instead
class Plugin {
public:
virtual void execute() = 0;
virtual ~Plugin() {}
};
3. Early in Development
Don’t use final prematurely before the design stabilizes.
// During prototyping - keep it flexible
class GameEntity {
public:
virtual void update();
virtual void render();
};
// Later, when design is stable, you might make specific methods final
class GameEntity {
public:
virtual void update();
virtual void render() final; // Now we know this shouldn't change
};
4. When Testing Requires Mocking
Classes that need to be mocked for unit testing.
// DON'T do this if you need to mock
class DatabaseConnection final { // BAD - cannot mock for testing
public:
void query(string sql);
};
// DO this instead
class DatabaseConnection {
public:
virtual void query(string sql);
virtual ~DatabaseConnection() {}
};
// Now you can create a mock for testing
class MockDatabaseConnection : public DatabaseConnection {
public:
void query(string sql) override {
// Mock implementation for testing
}
};
Best Practices and Guidelines
-
Use
finalconservatively - Only use it when you have a clear reason to prevent inheritance or overriding -
Document why - Add comments explaining why a class or method is final
// Final to prevent security vulnerabilities through inheritance class AuthenticationManager final { // ... }; -
Combine with
override- When marking a method final, use both keywords for clarityclass Derived : public Base { public: void method() override final { // Both override and final // ... } }; -
Consider alternatives - Sometimes composition is better than preventing inheritance
// Instead of making everything final class FinalClass final { void doWork(); }; // Consider composition class Worker { Helper helper; // Use composition instead public: void doWork() { helper.assist(); } }; -
Virtual destructors - If a class has virtual methods, ensure it has a virtual destructor
class Base { public: virtual void method() final; virtual ~Base() {} // Virtual destructor }; -
Performance considerations - Use
finalon hot-path methods to enable compiler optimizationsclass FastProcessor { public: virtual int compute(int x) final { return x * x; // Can be inlined aggressively } }; -
API design - For public APIs, think carefully before using
finalas it limits users -
Team communication - Discuss with team before making classes final in shared codebases