Skip to content
Educora
Intermediate20 min8 / 10

Inheritance and polymorphism

Derive new classes from existing ones, build polymorphism with `virtual` functions and `override`, and use abstract classes.

Check yourself
In this lesson you will learn
  • Create a derived class and call the base class constructor
  • Override methods with virtual and override
  • Explain how polymorphism works through base pointers and references
  • Create an abstract class with pure virtual functions

A game has a warrior, a mage and an archer. All of them have a name and health, and all of them attack — but each in its own way. Instead of repeating the same code in three classes, you can write the common part once in a base class and put the differences into derived classes. That is inheritance. Polymorphism then lets you treat them all the same way: you say “attack!”, and each character attacks in its own style.

Inheritance

class Warrior : public Character means “Warrior derives from Character”. The derived class receives all members of the base class and can add its own. protected members of the base class are closed to outside code but visible to derived classes. The derived class's constructor calls the base constructor in its initializer list.

C++
#include <iostream>
#include <string>

class Character {
public:
    Character(const std::string& name, int health)
        : name_(name), health_(health) {}

    void status() const {
        std::cout << name_ << " has " << health_ << " HP\n";
    }

protected:
    std::string name_;
    int health_;
};

class Warrior : public Character {
public:
    Warrior(const std::string& name) : Character(name, 150) {}

    void shieldUp() {
        health_ += 20;
        std::cout << name_ << " raises a shield\n";
    }
};

int main() {
    Warrior w("Elvin");
    w.status();
    w.shieldUp();
    w.status();
    return 0;
}
Expected output
Elvin has 150 HP
Elvin raises a shield
Elvin has 170 HP
status() comes ready-made from the base class, while shieldUp() exists only in Warrior.

Virtual functions

A derived object can be reached through a base class reference or pointer: every warrior is also a character. But which method is called then? For an ordinary method — the base class version, because the choice is made at compile time from the type of the reference. If the method is declared virtual, the choice is made while the program runs, based on the object's actual type.

C++
#include <iostream>

class Base {
public:
    virtual ~Base() = default;
    void hello() const { std::cout << "Base::hello\n"; }
    virtual void greet() const { std::cout << "Base::greet\n"; }
};

class Derived : public Base {
public:
    void hello() const { std::cout << "Derived::hello\n"; }
    void greet() const override { std::cout << "Derived::greet\n"; }
};

int main() {
    Derived d;
    const Base& ref = d;
    ref.hello();
    ref.greet();
    return 0;
}
Expected output
Base::hello
Derived::greet
hello is not virtual, so the reference type (Base) decides; greet is virtual, so the object's actual type (Derived) decides.

Abstract classes and polymorphism

Sometimes the base class does not know how to perform a method: “the area of a shape” cannot be computed in general. Such a method is declared pure virtual with = 0. A class with at least one pure virtual method is abstract: you cannot create objects of it, only derive from it. Then we can keep different shapes in one vector and treat them all the same way:

C++
#include <iostream>
#include <memory>
#include <vector>

class Shape {
public:
    virtual ~Shape() = default;
    virtual int area() const = 0;
};

class Rect : public Shape {
public:
    Rect(int w, int h) : w_(w), h_(h) {}
    int area() const override { return w_ * h_; }
private:
    int w_, h_;
};

class Triangle : public Shape {
public:
    Triangle(int b, int h) : b_(b), h_(h) {}
    int area() const override { return b_ * h_ / 2; }
private:
    int b_, h_;
};

int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Rect>(4, 3));
    shapes.push_back(std::make_unique<Triangle>(6, 5));
    int total = 0;
    for (const auto& s : shapes) {
        std::cout << "Area: " << s->area() << '\n';
        total += s->area();
    }
    std::cout << "Total: " << total << '\n';
    return 0;
}
Expected output
Area: 12
Area: 15
Total: 27
The loop does not know what kind of Shape it has, yet each object calls its own area() — that is polymorphism.
TermMeaning
base classthe class holding the common part (Shape)
derived classa class that derives from the base and extends it (Rect)
virtualthe method is chosen by the object's actual type
overrideasks the compiler to check that a virtual method is overridden
= 0a pure virtual method; the class becomes abstract

Key points

  • class B : public A — B derives from A and receives its members.
  • protected members are visible to derived classes but not to outside code.
  • A virtual method called through a base reference or pointer is chosen by the object's actual type.
  • override catches mistakes at compile time; a polymorphic base needs a virtual destructor.
  • A pure virtual method (= 0) makes a class abstract: no objects can be created from it.

Check yourself

10 questions. Every correct answer earns XP.

1 / 10
How do you derive the class Archer from Character?