OOP · Objects & interfaces

Virtual functions

Separate dynamic dispatch from the compiler’s override check.

virtual enables dispatch. override checks your intent.

A virtual member function participates in dynamic dispatch. A matching derived member overrides it. Writing override in the derived declaration asks the compiler to verify that this really is an override, catching mistakes in the parameter list or qualifiers.

Similar-looking functions can be different.

A const-qualified member treats ordinary data members as const through this; mutable members are an exception. Leaving const off a derived member changes its signature for overriding purposes. Without override, an accidental new function might compile while calls through the base interface keep using the base implementation.

An intentionally non-compiling override.

Derived::value is missing the const qualifier from Base::value. The override marker correctly rejects this declaration. Add const to the derived member to match the intended base operation.

struct Base {
    virtual int value() const { return 1; }
};
struct Derived : Base {
    int value() override { return 2; } // error
};

Two declarations, two responsibilities.

virtual starts the virtual dispatch contract in the base. override verifies a derived member participates in that contract. A matching override remains virtual even if it does not repeat the word virtual.

The base declares a virtual operation; the derived override marker asks the compiler to check the match.
virtual in Base
dynamic dispatch
override in Derived
compile-time check
  • virtual in Base leads to override in Derived (matching operation)

The base must offer a virtual operation.

A derived member cannot turn a non-virtual base member into a virtual call through the base interface. In a valid non-virtual example, calling through Base& selects the base member. Adding override to the derived member would instead reject the declaration.

Further reading

Trusted resources for the rules and APIs discussed in this guide.