Chapter 12: Inheritance & Polymorphism
Inheritance & Polymorphism in C++: The Real Power of OOP!
Hello my brilliant student! π Welcome to Lesson 12 β Inheritance & Polymorphism β the chapter that makes C++ feel like true object-oriented programming!
Up until now, we created classes that were completely independent. Today weβre going to learn how classes can inherit from each other β like a child inherits traits from parents β and how we can treat different objects in a uniform way through polymorphism.
Weβll cover everything very carefully, step by step:
- Single & multiple inheritance
- Virtual functions & the override keyword
- Pure virtual functions & abstract classes
- The final keyword
- Real-life analogies, tons of examples, common mistakes, and modern best practices
Letβs start building a family of classes!
1. What is Inheritance?
Inheritance lets one class (derived/child class) inherit members (variables & functions) from another class (base/parent class).
Real-life analogy:
- A Vehicle is the base class (has wheels, speed, colorβ¦).
- A Car and a Bike are derived classes β they inherit everything from Vehicle, but each adds its own special features (Car has doors, Bike has no doors but has pedals).
Syntax β Single Inheritance
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
class Base { // members... }; class Derived : public Base { // β public inheritance (most common) // additional members... }; |
Important types of inheritance (focus on public for now):
| Inheritance Type | Meaning |
|---|---|
| public | Public members stay public, protected stay protected (most common) |
| protected | Public & protected members become protected in derived |
| private | Public & protected members become private in derived (rare) |
Example β Single Inheritance
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <iostream> #include <string> class Vehicle { protected: // accessible to derived classes std::string brand; int year; public: Vehicle(std::string b, int y) : brand(std::move(b)), year(y) {} void displayInfo() const { std::cout << "Brand: " << brand << ", Year: " << year << "\n"; } }; class Car : public Vehicle { private: int doors; public: Car(std::string b, int y, int d) : Vehicle(b, y), doors(d) {} void honk() const { std::cout << brand << " says: Beep Beep!\n"; } void displayInfo() const { Vehicle::displayInfo(); // call base class version std::cout << "Doors: " << doors << "\n"; } }; int main() { Car myCar("Toyota", 2023, 4); myCar.displayInfo(); myCar.honk(); // Inherited members are accessible // myCar.year = 2024; // OK because protected in base return 0; } |
Output:
|
0 1 2 3 4 5 6 7 8 |
Brand: Toyota, Year: 2023 Doors: 4 Toyota says: Beep Beep! |
2. Multiple Inheritance
A class can inherit from more than one base class β powerful but can be dangerous (diamond problem).
Syntax:
|
0 1 2 3 4 5 6 7 8 |
class Derived : public Base1, public Base2 { // ... }; |
Example β Multiple Inheritance
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class Engine { protected: int horsepower; public: Engine(int hp) : horsepower(hp) {} void start() const { std::cout << "Engine started (" << horsepower << " hp)\n"; } }; class Wheels { protected: int count; public: Wheels(int c) : count(c) {} void roll() const { std::cout << count << " wheels rolling!\n"; } }; class Car : public Engine, public Wheels { public: Car(int hp, int wheels) : Engine(hp), Wheels(wheels) {} void drive() const { start(); roll(); std::cout << "Car is moving!\n"; } }; int main() { Car myCar(300, 4); myCar.drive(); } |
Output:
|
0 1 2 3 4 5 6 7 8 |
Engine started (300 hp) 4 wheels rolling! Car is moving! |
Warning: Multiple inheritance can cause the diamond problem (weβll cover virtual inheritance later if needed). Modern advice (2025+): Prefer composition over multiple inheritance whenever possible.
3. Polymorphism β Treating Different Objects the Same Way
Polymorphism means βmany formsβ β the ability to call the same function name on different objects and get different behavior.
This is achieved using virtual functions.
A. Virtual Functions & override
When you mark a function as virtual in the base class, derived classes can override it.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class Animal { public: virtual void makeSound() const { // β virtual! std::cout << "Some generic animal sound...\n"; } virtual ~Animal() = default; // Always make destructor virtual in base classes! }; class Dog : public Animal { public: void makeSound() const override { // β override keyword (C++11+) std::cout << "Woof Woof!\n"; } }; class Cat : public Animal { public: void makeSound() const override { std::cout << "Meow Meow!\n"; } }; int main() { Animal* pet1 = new Dog(); // β pointer to base, but points to derived! Animal* pet2 = new Cat(); pet1->makeSound(); // Woof Woof! β polymorphism! pet2->makeSound(); // Meow Meow! delete pet1; delete pet2; return 0; } |
Key points:
- Without virtual, it would call the base version (static binding).
- With virtual, it calls the derived version (dynamic binding / runtime polymorphism).
- Always use override keyword β compiler will catch mistakes!
- Always make base class destructor virtual if you plan to delete through base pointer.
4. Pure Virtual Functions & Abstract Classes
A pure virtual function has no implementation in the base class and must be overridden in derived classes.
A class with at least one pure virtual function is abstract β you cannot create objects of it.
Syntax:
|
0 1 2 3 4 5 6 |
virtual void function() const = 0; // = 0 means pure virtual |
Example β Shape Hierarchy
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
class Shape { public: virtual double area() const = 0; // pure virtual virtual double perimeter() const = 0; 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; } double perimeter() const override { return 2 * 3.14159 * radius; } }; 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; } double perimeter() const override { return 2 * (width + height); } }; int main() { // Shape s; // ERROR! Cannot instantiate abstract class Shape* shapes[2]; shapes[0] = new Circle(5.0); shapes[1] = new Rectangle(4.0, 6.0); for (auto shape : shapes) { std::cout << "Area: " << shape->area() << " | Perimeter: " << shape->perimeter() << "\n"; } delete shapes[0]; delete shapes[1]; return 0; } |
Beautiful result: One loop handles any shape β thatβs the power of polymorphism!
5. final Keyword β Preventing Further Overrides
C++11+ added final to stop inheritance or overriding.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Base { public: virtual void func() const {} }; class Derived : public Base { public: void func() const override final { // no one can override this anymore std::cout << "Final implementation!\n"; } }; class MoreDerived : public Derived { // void func() const override {} // ERROR! func is final }; |
You can also mark a class as final:
|
0 1 2 3 4 5 6 7 8 9 10 |
class MyFinalClass final { // ... }; // class DerivedFromFinal : public MyFinalClass {} // ERROR! |
Use final when you want to guarantee a certain behavior wonβt be changed.
Summary Table β Quick Reference
| Feature | Keyword(s) | Purpose |
|---|---|---|
| Single inheritance | : public Base | Inherit from one class |
| Multiple inheritance | : public Base1, public Base2 | Inherit from multiple |
| Runtime polymorphism | virtual + override | Call derived version through base pointer |
| Abstract class | = 0 (pure virtual) | Cannot instantiate, must be overridden |
| Prevent overriding | final | No further overrides allowed |
| Prevent inheritance | class MyClass final | No one can inherit from this |
Your Mini Homework (Try These!)
- Create a base class Employee with name, salary, and virtual function calculateBonus(). Derive Manager (20% bonus) and Developer (15% bonus).
- Make a pure virtual base class Drawable with pure virtual draw(). Derive Line, Circle, Rectangle and implement draw().
- Create a class hierarchy with at least one final function and one final class.
Youβre doing absolutely phenomenal! Youβve just unlocked runtime polymorphism β the feature that powers game engines, GUI frameworks, plugins, and almost every serious C++ application!
Next lesson: Templates β generic programming, the secret sauce behind STL!
Any questions? Confused about virtual vs non-virtual? Want more examples with pure virtual or multiple inheritance? Just ask β your friendly C++ teacher is right here for you! π
