C++

Inheritance & Polymorphism

Inheritance lets one class be built directly on top of another, reusing its data and behavior instead of duplicating it. Polymorphism is what makes that genuinely useful in practice: code written against the general, base version can automatically run the correct, more specific behavior for whatever actual type it's handed — without ever needing to know which one it is.

Quick reference
What it is
Inheritance: a class ("derived") built on top of another ("base"), automatically getting its members. Polymorphism: calling a method through a base-class reference and having the correct derived-class version actually run.
Mental model
Inheritance is "a Car is a Vehicle" — every car has everything a vehicle has, plus more. Polymorphism is being handed a box labeled "Vehicle" and having it behave correctly whether the actual thing inside is a Car or a Truck, without you checking which.
Key numbers
A method must be declared virtual in the base class for polymorphism to work — without it, C++ silently calls the base version instead of the derived one, a very common beginner surprise.
Reach for it when
Several related types share common data/behavior but each also needs its own distinct version of specific behavior — a family of related shapes, sensors, or UI widgets is the classic case.

Inheritance, in a worked example

class Sensor {
public:
    Sensor(const char *name) : name(name) {}
    void printName() { std::cout << name << std::endl; }

protected:
    const char *name;   // "protected" — accessible to Sensor AND anything derived from it
};

class TemperatureSensor : public Sensor {   // TemperatureSensor IS-A Sensor
public:
    TemperatureSensor() : Sensor("Temperature") {}   // calls the base constructor
    float readCelsius() { return 23.5f; }             // NEW behavior, specific to this sensor
};

TemperatureSensor t;
t.printName();          // inherited from Sensor, unchanged
t.readCelsius();         // TemperatureSensor's own, new method

TemperatureSensor automatically has everything Sensor has (name, printName()) without rewriting either, plus its own additional method. This is the direct reuse benefit of inheritance — the same relationship as a more specific C struct that happens to start with the exact same first fields as a more general one, except the compiler tracks and enforces the relationship for you.

virtual functions and polymorphism

Without virtual, calling a method through a base-class pointer always runs the base class's version, even if the actual object is a derived type — often not what you want:

class Shape {
public:
    virtual float area() { return 0; }   // virtual — enables polymorphism
};

class Circle : public Shape {
public:
    Circle(float r) : radius(r) {}
    float area() override { return 3.14159f * radius * radius; }   // overrides Shape's version
private:
    float radius;
};

class Square : public Shape {
public:
    Square(float s) : side(s) {}
    float area() override { return side * side; }
private:
    float side;
};

Shape *shapes[2] = { new Circle(5), new Square(4) };
for (int i = 0; i < 2; i++) {
    std::cout << shapes[i]->area() << std::endl;   // calls the RIGHT version for each, automatically
}
// 78.5397 (Circle), 16 (Square)

Every element of shapes is declared as a plain Shape*, but area() still runs each one's own real, specific implementation — that dispatch, decided at runtime based on the object's actual type, is exactly what "polymorphism" means.

The hierarchy, visually

Shape is the base class; Circle and Square both derive from it, each overriding area() with their own implementation Shape virtual area() Circle area() override Square area() override
Calling area() on a Shape* pointing at a Circle runs Circle's version — the base class only defines the shared contract, not the actual behavior.

Pure virtual functions and abstract classes

Shape::area() above had a (fairly meaningless) default implementation returning 0. A pure virtual function (= 0) instead says "every derived class must provide its own implementation — there is no sensible default":

class Shape {
public:
    virtual float area() = 0;   // pure virtual — no implementation at all
};
// Shape shape;   // COMPILE ERROR — Shape is now "abstract," cannot be instantiated directly
Circle c(5);        // fine — Circle provides area(), so it CAN be instantiated

A class with at least one pure virtual function is an abstract class — it exists purely to define a shared interface that concrete derived classes must fill in, and can never be instantiated on its own.

Real-world applications

Gotchas

Field notes