virtual, override and polymorphism
Through a reference to a base class, you can work with any derived class. But an ordinary method called through such a reference comes from the base class, even when the derived class declares its own:
#include <iostream>
struct Shape {
double area() const { return 0; }
};
struct Square : public Shape {
double side = 2;
double area() const { return side * side; }
};
int main() {
Square sq;
const Shape& s = sq;
std::cout << sq.area() << ' ' << s.area() << '\n'; // 4 0
}The compiler picks area by the type of the reference s, which is Shape, not by the object it refers to.
It is like a name badge. An ordinary call reads the badge: the reference says Shape, so Shape answers. To make the object itself answer, not the text on its badge, the method is made virtual.
Virtual functions
A method declared in the base class with the word virtual becomes a virtual function. When such a function is called through a reference or a pointer, the running program picks the version by the object's real type.
A derived class overrides a virtual function when it declares a method with the same name, the same parameter types and the same const. A call through a reference to the base class then runs its version.
1struct Shape {2 double area() const { return 0; }3};4struct Square : public Shape {5 double side = 2;6 double area() const { return side * side; }7};
Square sq;const Shape& s = sq;const Shape* p = &sq;Shape copy = sq;
The method is not virtual: the compiler picks area by the type of s, const Shape&, not by the object sq. Prints 0.
Switch virtual and the kind of call, and watch whose area the program runs. Note where it gets the version: from the type of the reference or from the object's own table.
This is polymorphism: one and the same call s.area() works differently for different shapes.
struct Shape {
virtual double area() const { return 0; }
};
struct Square : public Shape {
double side = 2;
double area() const override { return side * side; }
};With this Shape, the same main prints 4 4. The word override asks the compiler to check that the method really overrides a virtual function of the base class. If the name, the parameter types or the const do not match, the program does not compile.
A copy does not pick a version
Picking by the real type works only through a reference or a pointer. A Shape copy sliced off a Square object stays an ordinary Shape object, and calling area on it runs Shape::area. The copy has no badge: it is another object, and it really is a Shape.
Common mistake
The const is forgotten in the derived class:
struct Shape {
virtual double area() const { return 0; }
};
struct Square : public Shape {
double side = 2;
double area() { return side * side; } // ✗
};warning: 'Square::area' hides overloaded virtual function [-Woverloaded-virtual]Without const, this is a different function: it does not override Shape::area but hides it. The program builds, but a call through a reference to Shape still returns 0. With override, the compiler would have caught this, so write double area() const override.
Key points
- An ordinary method is picked by the type of the reference, a virtual one by the object's real type.
- Overriding needs the same name, the same parameter types and the same
const. overrideasks the compiler to check that the method really overrides a base-class function.- On a sliced copy, the base-class version is called.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.