CodeMIRAI
0 XP

Virtual functions

2 min read 3 runnable examples Quiz included

The inheritance lesson ended with a puzzle: a Circle used through a Shape& ran Shape's function. Mark the function virtual, and C++ instead looks at what the object really is when the program runs. One piece of code can then work with many types. That's polymorphism.

virtual and override

C++
#include <iostream>
#include <string>
using namespace std;

class Animal {
public:
    virtual string sound() const { return "..."; }
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    string sound() const override { return "Woof"; }
};

class Cat : public Animal {
public:
    string sound() const override { return "Meow"; }
};

void speak(const Animal& a) {
    cout << a.sound() << endl;      // which sound()? decided by the real object
}

int main() {
    Dog d;
    Cat c;
    Animal a;
    speak(d);
    speak(c);
    speak(a);
    return 0;
}
Output
Woof
Meow
...

speak knows nothing about dogs or cats, yet each animal makes its own sound, because sound is virtual. Through a reference or pointer to the base, a virtual call runs the version belonging to the object's actual class.

override tells the compiler you mean to replace a virtual function. If the signatures don't match (you forgot the const, say), it's an error instead of a silent new function:

C++
class Dog : public Animal {
    string sound() override { return "Woof"; }    // error: marked 'override', but does not override
};

Abstract classes

C++
#include <iostream>
#include <string>
using namespace std;

class Shape {
public:
    virtual double area() const = 0;          // pure virtual: no body here
    virtual string name() const = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const override { return 3.14159 * r * r; }
    string name() const override { return "circle"; }
};

class Rect : public Shape {
    double w, h;
public:
    Rect(double w, double h) : w(w), h(h) {}
    double area() const override { return w * h; }
    string name() const override { return "rectangle"; }
};

int main() {
    Circle c(1);
    Rect r(3, 4);
    Rect square(2, 2);
    Shape* shapes[] = {&c, &r, &square};

    double total = 0;
    for (Shape* s : shapes) {
        cout << s->name() << ": " << s->area() << endl;
        total += s->area();
    }
    cout << "total: " << total << endl;
    return 0;
}
Output
circle: 3.14159
rectangle: 12
rectangle: 4
total: 19.1416

= 0 makes a function pure virtual: Shape declares that every shape has an area but doesn't say how. A class with a pure virtual function is abstract, so you can't create a plain Shape (Shape s; doesn't compile). It exists to be derived from, and every concrete shape must supply its own area and name.

The loop is the payoff. It handles circles and rectangles alike, and a new Triangle class would work in it without changing a line.

Virtual destructors

C++
#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() { cout << "~Base" << endl; }
};

class Derived : public Base {
public:
    ~Derived() override { cout << "~Derived" << endl; }
};

int main() {
    Base* p = new Derived();
    delete p;          // deleting a Derived through a Base pointer
    return 0;
}
Output
~Derived
~Base

When an object is deleted through a pointer to its base, the destructor must be virtual so the derived part is cleaned up too. Without virtual, delete p would run only ~Base: the behaviour is undefined, and any memory or file the derived part owned would leak.

The rule: a class with any virtual function should have a virtual destructor. virtual ~Animal() = default; costs one line.

How it works

Each class with virtual functions has a hidden table of function pointers, and each object carries a pointer to its class's table. A virtual call looks the function up in that table, which is why it can depend on the real type. It costs one extra lookup per call, and nothing for functions that aren't virtual.

Add final to a class (class Dog final : public Animal) or a function to stop further overriding.

Finished this lesson?Mark it complete to earn +20 XP and move your progress on.