override and final
Contextual keywords override and final (C++11) let the compiler verify virtual function overrides and block further overriding or derivation.
override / finalsince C++11override and final are contextual specifiers that make the compiler verify that a member function overrides a base-class virtual function (override), and forbid further overriding of a virtual function or further derivation from a class (final).
Overview
Before C++11, overriding a virtual function was implicit: any derived-class member with the same name, parameter types, cv-qualifiers and ref-qualifiers as a base virtual function silently became an override. A typo in the signature, a missing const, or a later change to the base class produced a new virtual function (or a hiding non-virtual one) with no diagnostic. The program compiled, and calls through base pointers dispatched to the base implementation.
C++11 added two specifiers to close this gap:
overridestates intent. If the function does not override a virtual function in some base class, the program is ill-formed.finalstates a closure. On a virtual function, no derived class may override it. On a class, no class may derive from it.
Both are contextual keywords: they have special meaning only in the positions described below, so existing code that uses override or final as identifiers still compiles.
Syntax
struct Base {
virtual void f(int);
virtual void g() const;
virtual ~Base() = default;
};
struct D : Base {
void f(int) override; // C++11: checked against Base::f(int)
void g() const override final; // C++11: checked, and closed to further overriding
};
struct Leaf final : D {}; // C++11: no class may derive from LeafPlacement rules:
- In a member function declaration, both specifiers come after the declarator, after cv/ref-qualifiers and
noexcept, and before a trailing return type is not allowed β they follow it:auto f() const -> int override;. - For pure-specifier combinations:
void f() override = 0;andvoid f() override = default;(for special members such as a destructor) are valid in that order. - For classes,
finalappears after the class name:class X final : public Base { };. Afinalclass may not be a base, though it may itself derive from others. overrideandfinalmay be combined in either order.finalon a function that is not virtual and does not override anything is an error;finalimplies the function is virtual only if it overrides one.- The specifiers appear only in the declaration inside the class. An out-of-class definition omits them.
virtualis optional on an overrider, since the function is implicitly virtual. Withoverridepresent, repeatingvirtualis redundant.
Examples
Catching signature drift
struct Shape {
virtual double area() const = 0;
virtual void scale(double factor);
virtual ~Shape() = default;
};
struct Circle : Shape {
explicit Circle(double r) : r_(r) {}
double area() const override { return 3.14159265 * r_ * r_; }
// void scale(float) override; // error: does not override (parameter is float)
// double area() override; // error: missing const -> different signature
void scale(double factor) override { r_ *= factor; }
private:
double r_;
};Without override, both commented declarations would compile as unrelated members and Shape::scale would be called through a Shape&. Many compilers also provide a warning for a missed override (-Wsuggest-override in GCC/Clang, -Winconsistent-missing-override in Clang) when some, but not all, overriders use the specifier. Enabling these makes the habit enforceable.
Refactoring safety
struct Handler {
virtual void on_event(int id); // later changed to: on_event(int id, bool sync)
virtual ~Handler() = default;
};
struct Logger : Handler {
void on_event(int id) override; // now a hard error after the base changes
};When the base signature changes, every stale overrider fails to compile, which is exactly what you want in a large hierarchy.
Sealing a function and a class
class Connection {
public:
virtual void open();
virtual void close();
virtual ~Connection() = default;
};
class TlsConnection : public Connection {
public:
void open() override;
void close() override final; // C++11: subclasses may customize open() but not close()
};
class PinnedTlsConnection final : public TlsConnection {
public:
void open() override; // OK
// void close() override; // error: Connection::close is final in TlsConnection
};
// class Foo : public PinnedTlsConnection {}; // error: base is finalDevirtualization
struct Codec {
virtual int decode(const char*, int) = 0;
virtual ~Codec() = default;
};
struct Utf8Codec final : Codec {
int decode(const char* p, int n) override;
};
int run(Utf8Codec& c, const char* p, int n) {
return c.decode(p, n); // compiler may call Utf8Codec::decode directly, even inline
}Because Utf8Codec is final, the dynamic type of a Utf8Codec& is known exactly, so the compiler can replace the vtable dispatch with a direct call. The same applies to a final function called through a pointer or reference to a class that declares it. This is a permitted optimization, not a guarantee; modern compilers can often devirtualize without final using whole-program or LTO information, but final makes it reliable.
Destructors
struct Base { virtual ~Base() = default; };
struct Derived : Base {
~Derived() override = default; // C++11: legal; checks that Base has a virtual destructor
};Using override on a destructor is valid and turns a missing virtual in the base into a compile error. It is a useful check, though some style guides prefer to omit it on destructors.
Best Practices
- Use
overrideon every overrider. Treat a missing specifier as a defect. Enable-Wsuggest-override(GCC) or-Winconsistent-missing-override(Clang, on by default), or MSVC's C5204/C4263/C4264 family as appropriate. - Do not combine
virtualwithoverride. The core guidelines recommend exactly one ofvirtual,override, orfinalon a function declaration, withoverride/finalfor derived classes.override finaltogether is acceptable when you want to state both. - Prefer
finalon leaf classes that are not designed for inheritance. It documents intent and enables devirtualization. - Be careful sealing library types. A
finalclass cannot be mocked by subclassing or extended by clients. Seal in application code freely; in public APIs, do so deliberately. - Seal individual functions sparingly when a template-method pattern requires an invariant step (e.g.
close()that always releases a resource). - Mark
finalnon-virtual helpers' classes, not functions.finalon a function only makes sense for a virtual one.
Common Pitfalls
- Assuming
overridefixes hiding. It only checks the declaration it is attached to. A same-named overload with a different signature that lacksoverridestill hides base overloads; useusing Base::f;to bring them back into scope. - Const and ref-qualifier mismatches.
void f() constandvoid f()are different signatures, as arevoid f() &andvoid f() &&. These are the most common silent non-overrides in pre-C++11 code. - Covariant return types are still allowed. An overrider may return a pointer or reference to a more derived class, and
overrideaccepts it. Changing to an unrelated return type is an error. - Different default arguments. Default arguments are bound statically by the declared type of the expression. An overrider with different defaults compiles with
override, yet calls through a base reference use the base defaults. Avoid defaults on virtual functions. - Wrong
noexcept. An overrider may not have a looser exception specification than the base function. Since C++17,noexceptis part of the function type, but the override rule still requires the overrider to be at least as restrictive. - Ordering of specifiers.
void f() override const;is ill-formed; cv- and ref-qualifiers precedeoverride/final, andnoexceptprecedes them too. - Private overriders. Access control is checked on the static type, not the dynamic one, so a private overrider still dispatches through a public base declaration.
overridedoes not change this. finalis notconstfor design. Marking a classfinalafter clients have already derived from it is a breaking change.- Using the names elsewhere. Because they are contextual,
int override = 0;is legal. It is poor style and confuses readers and tooling; avoid it.
See Also
- Abstract classes: pure virtual functions, which
overridecan implement andfinalcan seal.