final and override are two context-sensitive identifiers introduced in C++11, used in virtual-function inheritance to explicitly express the intent of overriding and sealing, allowing the compiler to surface polymorphism mismatches at compile time that would otherwise only show up as runtime bugs.
| Book | Video | Code | X |
|---|---|---|---|
| cppreference-final / cppreference-override / markdown | Video Explanation | Practice Code |
Why were they introduced?
- Before C++11, whether a derived class actually overrode a base virtual function relied entirely on programmers checking signatures by hand — a single mismatched parameter would silently turn an override into a name-hiding declaration with no compiler warning
- There was no standard way to express the design intent that "this type, or this polymorphic chain, ends here"
- They make the design contract of virtual functions readable and verifiable
What's the difference between the two?
- override: applied after a derived-class member function, explicitly declaring "this function overrides a base-class virtual function", so the compiler can verify it
- final: applied after a virtual function means "this virtual function cannot be further overridden"; applied after a class means "this class cannot be inherited from"
Without override, even a typo in the derived signature only becomes "a brand-new ordinary function" — the polymorphic behavior is silently lost.
struct Base {
virtual void func(int) { }
};
struct Derived : Base {
void func(double) { } // intended to override, but the parameter type is wrong;
// this actually declares a new function
};With override, the same mistake is rejected at compile time.
struct Derived : Base {
void func(double) override; // error: no matching virtual function in any base class
};Only a base virtual function whose signature (return type + parameter list + cv-qualifiers + ref-qualifiers) matches exactly will satisfy override.
struct Base {
virtual void func(int);
};
struct Derived : Base {
void func(int) override; // ok
};final has two usages targeting different things.
On a virtual function - cut off the polymorphic chain
struct A {
virtual void func() final { }
};
struct B : A {
void func() override; // error: A::func is final and cannot be overridden
};On a class - forbid inheritance
struct B final { };
struct C : B { }; // error: B is final and cannot be inherited fromLock the outer interface with virtual ... final, and expose the customizable steps as pure virtual functions. The result is a stable interface where the execution order cannot be changed but each step is customizable. This is a concise expression of the Non-Virtual Interface idiom.
struct AudioPlayer {
virtual void play() final { // subclasses cannot change the overall flow of play
init_audio_params();
play_audio();
}
private:
virtual void init_audio_params() = 0; // left for subclasses to customize
virtual void play_audio() = 0;
};
struct WAVPlayer : AudioPlayer {
void init_audio_params() override { /* ... */ }
void play_audio() override { /* ... */ }
};
struct MP3Player : AudioPlayer {
void init_audio_params() override { /* ... */ }
void play_audio() override { /* ... */ }
};Callers always use the unified AudioPlayer::play(); each format's player only needs to implement the two hooks. This structure is common when designing plugin-style or protocol-style interfaces.
Neither override nor final is a reserved word or a keyword — they are context-sensitive identifiers. They only carry these meanings when they appear at specific positions in a virtual function declaration or a class declaration; in any other position they can still be used as variable names, type names, namespace names, etc.
B override; // ok: here override is just an ordinary variable name
B final; // ok: here final is just an ordinary variable nameThis is a deliberate compromise in the C++ standard for backward compatibility: existing code that uses override or final as identifiers won't fail to compile after upgrading to C++11.
Once override is added to a derived-class member function, the compiler requires a virtual function with a matching signature to exist in some base class — otherwise it's a compile error. This is the core value of override: lifting "override mismatch" silent bugs from runtime to compile time.
struct A {
virtual void func1() { }
void func2() { } // note: not virtual
};
struct B : A {
void func1() override; // ok
void func2() override; // error: A::func2 is not virtual
};A final class cannot be inherited from at all, not even to add a couple of helper methods. Marking a class final is essentially committing to "this type is, by design, a leaf node". Some rules of thumb:
- The type is explicitly not meant to be extended further (e.g. error types, framework-internal implementation classes, singletons) -> a good fit for final
- A general-purpose base class or a framework-provided extension point -> do not casually add final
An ordinary member function cannot be overridden in the first place, so adding final to it is meaningless and the compiler will reject it.
struct A {
void func() final; // error: final cannot be applied to a non-virtual function
};If a virtual function should both override the base-class version and forbid further overriding in derived classes, you can combine the two.
struct B : A {
void func() override final; // overrides A::func, and prevents C from overriding it again
};- 0 - Familiarize with override
- 1 - Familiarize with final
- 2 - final + Template Method - AudioPlayer / WAV / MP3 / OGG
d2x checker final-and-override