Skip to content
Theory · Classes and OOP · lesson 10 of 15

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:

C++
#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.

LiveWhich area runss.area() → Shape::area
s.area() prints: 0
sizeof(Shape) = 1, sizeof(Square) = 8 on x86-64; in course exercises (wasm32), 1 and 8

The method is not virtual: the compiler picks area by the type of s, const Shape&, not by the object sq. Prints 0.

Turn virtual on and compare s.area() with copy.area()

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.

C++
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:

C++
struct Shape {
    virtual double area() const { return 0; }
};
struct Square : public Shape {
    double side = 2;
    double area() { return side * side; }  // ✗
};
TEXT
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.
  • override asks 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.

Sign in another way