12.2a — The override and final specifiers, and covariant return types

To address some common challenges with inheritance, C++11 added two special identifiers to C++: override and final. Note that these identifiers are not considered keywords -- they are normal identifiers that have special meaning in certain contexts.

Although final isn’t used very much, override is a fantastic addition that you should use regularly. In this lesson, we’ll take a look at both, as well as one exception to the rule that virtual function override return types must match.

The override specifier

As we mentioned in the previous lesson, a derived class virtual function is only considered an override if its signature and return types match exactly. That can lead to inadvertent issues, where a function that was intended to be an override actually isn’t.

Consider the following example:

Because rBase is an A reference to a B object, the intention here is to use virtual functions to access B::getName1() and B::getName2(). However, because B::getName1() takes a different parameter (a short int instead of an int), it’s not considered an override of A::getName1(). More insidiously, because B::getName2() is const and A::getName2() isn’t, B::getName2() isn’t considered an override of A::getName2().

Consequently, this program prints:


In this particular case, because A and B just print their names, it’s fairly easy to see that we messed up our overrides, and that the wrong virtual function is being called. However, in a more complicated program, where the functions have behaviors or return values that aren’t printed, such issues can be very difficult to debug.

To help address the issue of functions that are meant to be overrides but aren’t, C++11 introduced the override specifier. Override can be applied to any override function by placing the specifier in the same place const would go. If the function does not override a base class function, the compiler will flag the function as an error.

The above program produces two compile errors: one for B::getName1(), and one for B::getName2(), because neither override a prior function. B::getName3() does override A::getName3(), so no error is produced for that line.

There is no performance penalty for using the override specifier, and it helps avoid inadvertent errors. Consequently, we highly recommend using it for every virtual function override you write to ensure you’ve actually overridden the function you think you have.

Rule: Apply the override specifier to every intended override function you write.

The final specifier

There may be cases where you don’t want someone to be able to override a virtual function, or inherit from a class. The final specifier can be used to tell the compiler to enforce this. If the user tries to override a function or class that has been specified as final, the compiler will give a compile error.

In the case where we want to restrict the user from overriding a function, the final specifier is used in the same place the override specifier is, like so:

In the above code, B::getName() overrides A::getName(), which is fine. But B::getName() has the final specifier, which means that any further overrides of that function should be considered an error. And indeed, C::getName() tries to override B::getName() (the override specifier here isn’t relevant, it’s just there for good practice), so the compiler will give a compile error.

In the case where we want to prevent inheriting from a class, the final specifier is applied after the class name:

In the above example, class B is declared final. Thus, when C tries to inherit from B, the compiler will give a compile error.

Covariant return types

There is one special case in which a derived class virtual function override can have a different return type than the base class and still be considered a matching override. If the return type of a virtual function is a pointer or a reference to a class, override functions can return a pointer or a reference to a derived class. These are called covariant return types. Here is an example:

Note that some older compilers (e.g. Visual Studio 6) do not support covariant return types.

One interesting note about covariant return types: In the above example, if you call getThis() with a Base (even if the Base is a pointer or a reference to a Derived), the object being returned will still be a Base*. This is because C++ can’t dynamically select types, so you’ll always get the type that matches the most base version of the function being called.

You’ll only get a Derived* if you call getThis() with an object that is already a Derived.

12.3 -- Virtual destructors, virtual assignment, and overriding virtualization
12.2 -- Virtual functions and polymorphism

13 comments to 12.2a — The override and final specifiers, and covariant return types

  • Omri

    "Note that these identifiers are not considered keywords -- they are normal identifiers that have special meaning in certain contexts."
    How can identifiers, that are a part of the language, not be considered key words?
    Does this mean, for example, that I can define a variable named final or overload and this will work?
    Are there more such identifiers that are not keywords?

    • Alex

      > Does this mean, for example, that I can define a variable named final or overload and this will work?

      Yes, that is exactly what it means. There are no other such identifiers at this time. But it’s possible new ones could be added in future iterations of C++.

  • Hardik


    This Prints "I AM BASE" but as per the function getThis() It should return a ptr/reference to derived class which in turn, should have printed "I AM DERIVED".
    Is it like that Because (this) return the object being pointed to and here, that object is base, due to which it prints “I AM BASE” and also, Derived* just tells that the return type should be a matching to Derived class and since, base matches it, there’s no error too !
    Help Please !
    Thanks In Advance ! 🙂

    • Alex

      Although you might think base.getThis() would return a Derived* since base is a reference to a Derived, in this case even though the Derived::getThis() is called, a Base* is returned. Then, because getDerived() is non-virtual, the Base version of getDerived() is called.

      A Derived* is only returned if the object calling getThis() is actually a Derived. If you’re calling getThis() from a Base object, you’ll get a Base* because the Base version of getThis() returns a Base*.

      This limitation exists because C++ is not able to dynamically select types.

  • Sydney

    I’m a little confused as to the usefulness of the override specifier. In the example below we mention that two of the functions won’t work as overrides with the command.

    Wouldn’t it then just be enough to make sure all the override functions have the same signature and return type, with pointers to the Base and Derived classes as exceptions?

    • Alex

      The whole point of the override specifier is to help ensure that your overrides DO have the same signature and return type. It’s very easy to think they are the same but overlook some minor detail that makes them different.

      I can’t tell you how many hours I’ve wasted debugging code that didn’t work, only to find out that my override had a minor difference (e.g. an added or missing const) so it was being treated as a separate function rather than an override of a base function.

      • Sydney

        I see. I was under the impression the override specifier was supposed to "override" the restrictions on what was an override function. It makes more sense though that it’s meant to force the compiler to consider the function as an override, serving as a check in case you forget to make it one. Thanks.

  • Gajendra Gulgulia

    can you please tell once more what is the purpose of "const" keyword after the function parameter, line in your class B:public A example above?

    • Alex

      It means the member function is a const member function. Const member functions promise not to modify the value of member variables, and can only call other const member functions (or non-member functions).

      • luke

        It took me a moment to remember what the purpose of const was in that place too.

        A suggestion: a quick note / appendix on what const does in all its different possible positions would be really helpful as it can sometimes be confusing.

        I put together one myself that I use for reference:

        thanks for all the great tutorials!!

  • Prado

    Amazing tutorials!

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter