Module 9: Inheritance and Polymorphism
Chapter 9 • Intermediate
Inheritance and Polymorphism
Inheritance and polymorphism are core OOP concepts that enable code reuse and flexible design. They're essential for building scalable, maintainable C++ applications.
What is Inheritance?
Inheritance allows a class (derived/child class) to inherit properties and behaviors from another class (base/parent class). This promotes code reuse and establishes an "is-a" relationship.
Key Benefits:
- Code Reuse: Don't repeat code from base class
- Hierarchical Organization: Model real-world relationships
- Extensibility: Add new features without modifying base class
- Polymorphism: Use derived classes through base class pointers
Inheritance Syntax
Basic Structure:
class BaseClass {
// Base class members
};
class DerivedClass : access-specifier BaseClass {
// Derived class members
};
Access Specifiers:
- public: Public and protected members remain accessible
- protected: Public members become protected
- private: Public and protected members become private
Most Common: Use public inheritance.
Simple Inheritance Example
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
void eat() {
cout << name << " is eating" << endl;
}
void sleep() {
cout << name << " is sleeping" << endl;
}
};
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void bark() {
cout << name << " is barking" << endl;
}
};
int main() {
Dog dog("Buddy");
dog.eat(); // Inherited from Animal
dog.sleep(); // Inherited from Animal
dog.bark(); // Dog's own method
return 0;
}
Access Levels in Inheritance
| Base Class | Inheritance Type | Derived Class Access |
|---|---|---|
| public | public | public |
| protected | public | protected |
| private | public | Not accessible |
| public | protected | protected |
| protected | protected | protected |
| private | protected | Not accessible |
Constructor and Destructor in Inheritance
Order of Execution:
- Base class constructor
- Derived class constructor
- Derived class destructor
- Base class destructor
Example:
class Base {
public:
Base() { cout << "Base constructor" << endl; }
~Base() { cout << "Base destructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor" << endl; }
~Derived() { cout << "Derived destructor" << endl; }
};
Types of Inheritance
1. Single Inheritance
One base class, one derived class:
class A { };
class B : public A { };
2. Multiple Inheritance
Derived class inherits from multiple base classes:
class A { };
class B { };
class C : public A, public B { };
Note: Can lead to ambiguity issues (diamond problem).
3. Multilevel Inheritance
Chain of inheritance:
class A { };
class B : public A { };
class C : public B { };
4. Hierarchical Inheritance
Multiple derived classes from one base:
class A { };
class B : public A { };
class C : public A { };
Function Overriding
Derived class redefines base class function:
class Animal {
public:
void makeSound() {
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() { // Override
cout << "Dog barks" << endl;
}
};
Note: Without virtual functions, this is function hiding, not true polymorphism.
Virtual Functions
Enable runtime polymorphism (late binding):
class Animal {
public:
virtual void makeSound() { // Virtual function
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override { // Override virtual function
cout << "Dog barks" << endl;
}
};
Key Points:
virtualkeyword enables polymorphismoverridekeyword (C++11) ensures correct overriding- Function called based on object type, not pointer type
Polymorphism Example
Animal* animal1 = new Dog("Buddy");
animal1->makeSound(); // Calls Dog::makeSound() - polymorphism!
Pure Virtual Functions
Functions with no implementation in base class:
class Animal {
public:
virtual void makeSound() = 0; // Pure virtual
};
Effects:
- Makes class abstract (cannot instantiate)
- Derived classes MUST implement it
- Creates interface contract
Abstract Classes
Classes with at least one pure virtual function:
class Shape {
public:
virtual double getArea() = 0; // Pure virtual
virtual double getPerimeter() = 0; // Pure virtual
};
// Cannot do: Shape s; // Error: abstract class
Virtual Destructors
Critical Rule: Always make base class destructor virtual!
class Base {
public:
virtual ~Base() { // Virtual destructor
cout << "Base destructor" << endl;
}
};
class Derived : public Base {
public:
~Derived() {
cout << "Derived destructor" << endl;
}
};
Why? Ensures proper cleanup when deleting through base pointer.
Virtual Function Table (vtable)
How polymorphism works internally:
- Each class with virtual functions has a vtable
- Contains pointers to virtual functions
- Object stores pointer to vtable
- Runtime lookup determines which function to call
Function Hiding vs Overriding
Function Hiding (without virtual):
Base* ptr = new Derived();
ptr->function(); // Calls Base::function()
Function Overriding (with virtual):
Base* ptr = new Derived();
ptr->function(); // Calls Derived::function()
Multiple Inheritance Issues
Diamond Problem
class A { };
class B : public A { };
class C : public A { };
class D : public B, public C { }; // Two copies of A!
Solution: Virtual Inheritance
class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { }; // One copy of A
Best Practices
- ✅ Use public inheritance for "is-a" relationships
- ✅ Make destructors virtual in base classes
- ✅ Use override keyword (C++11) for clarity
- ✅ Prefer composition over inheritance when appropriate
- ✅ Use abstract classes to define interfaces
- ✅ Avoid deep inheritance hierarchies (keep it shallow)
- ✅ Use virtual inheritance to solve diamond problem
- ✅ Document inheritance relationships clearly
Common Mistakes
- ❌ Forgetting virtual destructor in base class
- ❌ Using inheritance for "has-a" relationships (use composition)
- ❌ Deep inheritance hierarchies (hard to maintain)
- ❌ Not understanding virtual function overhead
- ❌ Diamond problem without virtual inheritance
- ❌ Hiding instead of overriding (missing virtual)
Next Module
In Module 10, we'll learn about STL (Standard Template Library) - powerful containers, algorithms, and iterators that make C++ programming efficient!
Hands-on Examples
Basic Inheritance
#include <iostream>
#include <string>
using namespace std;
class Animal {
protected:
string name;
int age;
public:
Animal(string n, int a) : name(n), age(a) {
cout << "Animal constructor" << endl;
}
void eat() {
cout << name << " is eating" << endl;
}
void sleep() {
cout << name << " is sleeping" << endl;
}
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
class Dog : public Animal {
private:
string breed;
public:
Dog(string n, int a, string b) : Animal(n, a), breed(b) {
cout << "Dog constructor" << endl;
}
void bark() {
cout << name << " is barking" << endl;
}
void display() {
Animal::display(); // Call base class method
cout << "Breed: " << breed << endl;
}
};
int main() {
Dog dog("Buddy", 3, "Golden Retriever");
dog.eat(); // Inherited from Animal
dog.sleep(); // Inherited from Animal
dog.bark(); // Dog's own method
dog.display(); // Dog's overridden method
return 0;
}Inheritance allows Dog to inherit members from Animal. Protected members are accessible in derived class. Derived class can add new members and override base class methods. Base constructor called before derived constructor.
Practice with Programs
Reinforce your learning with hands-on practice programs
Related Program Topics
Recommended Programs
Hello World
BeginnerThe classic first program that prints "Hello World" to the console
Display Your Name
BeginnerProgram to display your name on the screen
User Input
BeginnerProgram to take input from user and display it
Array Max & Min
BeginnerProgram to find maximum and minimum element in an array
Array Average
BeginnerProgram to calculate average of array elements