</>
Skip to content
C++ lessons (30/42)

C++ — Polymorphism

Virtual functions

#include <iostream>
using namespace std;

class Shape {
public:
    virtual double area() const { return 0; }
    virtual string type() const { return "Shape"; }
    virtual ~Shape() = default;
};

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

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    double area() const override { return width * height; }
    string type() const override { return "Rectangle"; }
};

void printArea(const Shape &s) {
    cout << s.type() << " area: " << s.area() << endl;
}

int main() {
    Circle c(5);
    Rectangle r(4, 6);

    printArea(c); // Circle area: 78.5398
    printArea(r); // Rectangle area: 24

    // Polymorphism with pointers
    Shape *shapes[] = {&c, &r};
    for (const auto *s : shapes) {
        printArea(*s);
    }

    return 0;
}

Pure virtual functions and abstract classes

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() const = 0; // Pure virtual
    virtual ~Animal() = default;
};

// class Animal a; // Error: can't instantiate abstract class

class Dog : public Animal {
public:
    void speak() const override { cout << "Woof!" << endl; }
};

class Cat : public Animal {
public:
    void speak() const override { cout << "Meow!" << endl; }
};

int main() {
    // Animal a; // Error
    Dog dog;
    Cat cat;

    dog.speak();
    cat.speak();

    // Array of base class pointers
    Animal *zoo[] = {&dog, &cat};
    for (const auto *a : zoo) {
        a->speak();
    }

    return 0;
}

Virtual destructor

#include <iostream>
using namespace std;

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

class Derived : public Base {
private:
    int *data;
public:
    Derived() : data(new int[100]) {
        cout << "Derived constructed" << endl;
    }
    ~Derived() override {
        delete[] data;
        cout << "Derived destroyed" << endl;
    }
};

int main() {
    Base *ptr = new Derived();
    delete ptr; // Without virtual destructor: only Base destructor called
    return 0;
}

Abstract class with interface

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

class Serializable {
public:
    virtual string serialize() const = 0;
    virtual ~Serializable() = default;
};

class Loggable {
public:
    virtual string toLogString() const = 0;
    virtual ~Loggable() = default;
};

class User : public Serializable, public Loggable {
private:
    string name;
    int age;
public:
    User(string n, int a) : name(n), age(a) {}

    string serialize() const override {
        return "{\"name\":\"" + name + "\",\"age\":" + to_string(age) + "}";
    }

    string toLogString() const override {
        return "[USER] " + name + " (age " + to_string(age) + ")";
    }
};

int main() {
    User u("Alice", 30);
    cout << u.serialize() << endl;
    cout << u.toLogString() << endl;
    return 0;
}

Dynamic casting

#include <iostream>
using namespace std;

class Animal {
public:
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void fetch() { cout << "Fetching!" << endl; }
};

class Cat : public Animal {
public:
    void purr() { cout << "Purring!" << endl; }
};

int main() {
    Animal *a = new Dog();

    // dynamic_cast checks at runtime
    Dog *dog = dynamic_cast<Dog *>(a);
    if (dog) {
        dog->fetch(); // OK
    }

    Cat *cat = dynamic_cast<Cat *>(a);
    if (cat) {
        cat->purr(); // Never reached
    } else {
        cout << "Not a cat" << endl;
    }

    delete a;
    return 0;
}

Typeid

#include <iostream>
#include <typeinfo>
using namespace std;

class Base {
public:
    virtual ~Base() = default;
};

class Derived : public Base {};

int main() {
    Base *ptr = new Derived();

    cout << "Type: " << typeid(*ptr).name() << endl;
    cout << "Is Derived? " << (typeid(*ptr) == typeid(Derived)) << endl;

    delete ptr;
    return 0;
}

CRTP (Curiously Recurring Template Pattern)

#include <iostream>
using namespace std;

template <typename Derived>
class Counter {
    static int count;
public:
    Counter() { count++; }
    ~Counter() { count--; }
    static int getCount() { return count; }
};

template <typename Derived>
int Counter<Derived>::count = 0;

class Dog : public Counter<Dog> {};
class Cat : public Counter<Cat> {};

int main() {
    Dog d1, d2;
    Cat c1;
    cout << "Dogs: " << Dog::getCount() << endl;  // 2
    cout << "Cats: " << Cat::getCount() << endl;  // 1
    return 0;
}

Mini Practice

Write C++ code that:

  1. Creates an abstract Vehicle class with pure virtual start() and stop()
  2. Implements Car and Motorcycle derived classes
  3. Uses dynamic_cast to check the actual type at runtime
  4. Demonstrates virtual destructor importance

Up Next

In the next lesson, you'll learn about Templates — generic programming in C++.

Related Topics

Frequently Asked Questions about Polymorphism

What is Polymorphism in C++?

Polymorphism is a fundamental concept in C++. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Polymorphism?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Polymorphism.

Why is Polymorphism important in C++?

Polymorphism is essential for C++ development. Understanding this concept will help you write better code and solve real-world problems more effectively.