4 minutes
Declaring Overriding Functions override (Notes)
NOTE
My notes on Chapter 3, Item 12 of Effective Modern C++ written by Scott Meyers.
Some (or even all) of the text can be similar to what you see in the book, as these are notes: I’ve tried not to be unnecessarily creative with my words. :)
Overriding != Overloading
Example of virtual function overriding:
// Base class
class Base {
public:
virtual void doWork();
// ...
};
// Derived class from Base
class Derived: public Base {
public:
// virtual is optional
// this will "override" Base::doWork
virtual void doWork();
// ...
};
// This creates a "Base" class pointer to "Derived" class object
std::unique_ptr<Base> upb = std::make_unique<Derived>();
// Derived doWork() function is invoked
upb->doWork();
This is how virtual function overriding allows to invoke a “derived class function” from a base class interface.
Requirements for overriding:
- Base class function must be virtual.
- Base and derived function names must be identical (except for destructors).
- Parameter types of base and derived functions must be identical.
- Constness of both base and derived functions must be identical.
- The return types and exception specifications of the base and derived functions must be compatible.
- [from C++11] Both functions should have identical reference qualifiers (see below)
Reference qualifiers were new to me, but the book gives a really good example:
// Useful when you want to limit the calls to lvalues or rvalues only
class Widget {
public:
// ...
void doWork() &; // Only when *this is an lvalue
void doWork() &&; // Only when *this is an rvalue
// ...
};
// Suppose there is a makeWidget() function that returns an instance of Widget class, this will be an rvalue here
Widget makeWidget();
// lvalue
Widget w;
// this will call void doWork() &;
w.doWork();
// this will call void doWork() &&;
makeWidget().doWork();
In case the functions don’t have identical reference qualifiers, the derived class will still have the function, but it won’t override the base class function.
Because of a lot of conditions (see 6 requirements above), it’s easy to make mistakes or forget a few things while overriding a function. And the book states that it’s not worth expecting from compiler to report an error, because the code can still be valid - it’s just you expected it to override, but it didn’t.
See an example below:
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
None of the functions in Derived
class will override functions in the Base
class. Why?
mf1()
: declaredconst
inBase
but not inDerived
.mf2()
: argint
inBase
butunsigned int
inDerived
.mf3()
: lvalue-qualified inBase
but rvalue-qualified inDerived
.mf4()
: isn’t declaredvirtual
inBase
.
To avoid worrying about human mistakes, it’s better to let the compiler know explicitly that these functions are expected to override. For that, declare the functions in Derived
class override
.
class Derived: public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x) override;
virtual void mf3() && override;
virtual void mf4() const override;
};
Now, this won’t compile for sure no matter what compiler you are using.
C++-11 introduced 2 contextual keywords: final
(see note below), override
. These keywords are reserved, but only in certain contexts.
Note: In case you want to prevent a function in a base class to be overridden in derived classes, you can declare them final
. final
can also be declared for a class, in which case it won’t be allowed to be used a base class.
For override
: In case your old (legacy) code uses override
somewhere else, it’s fine - keep it! Only when it’s used at the end of a member function declaration, then it’s reserved.
class Sample {
public:
// legal
void override();
}
The chapter (item) ends with a brief about lvalue and rvalue reference member functions, which I would like to cover in another blog. For this blog, I think this should be good! :)
Thank you for reading.