Paramclasses Academy - Blog

Tutorials Details

What is Inheritance in C++

What is Inheritance in C++

October 16, 2026

Welcome to Module 12 of Param Classes. In this module, you’ll learn how inheritance lets one C++ class reuse and extend the features of another class. You’ll progress from basic syntax to inheritance types, constructor order, function overriding, and a practical mini-project.

Learning objectives

By the end of this module, you will be able to:

  • Explain base classes and derived classes.

  • Implement public, protected, and private inheritance.

  • Identify single, multilevel, multiple, and hierarchical inheritance.

  • Understand constructor and destructor execution order.

  • Override inherited functions using override.

  • Recognize how inheritance supports code reuse and runtime polymorphism.

    Prerequisites

    Classes, objects, constructors, access specifiers

    Estimated time

    60–90 minutes

1. What is inheritance?

Inheritance is an object-oriented programming feature that allows a new class to acquire accessible members of an existing class and add its own functionality.

Consider a program that manages people:

  • A Person has a name and age.

  • A Student has a name, age, and student ID.

  • A Teacher has a name, age, and subject.

Instead of independently implementing the common properties in every class, you can define Person once and derive Student and Teacher from it.

Person

name · age · introduce()

Student

studentId

Teacher

subject

Conceptual relationship: Student and Teacher are specialized kinds of Person.

Important terminology:

  • Base class (parent class): the class being inherited from, such as Person.

  • Derived class (child class): the class that inherits from the base class, such as Student.

  • Code reuse: shared functionality can be implemented once and reused by derived classes.

  • Specialization: a derived class can introduce features specific to its role.

Inheritance does not mean every base-class member becomes directly accessible. Accessibility depends on the member's access specifier and the inheritance mode.

2. Basic inheritance syntax

The general syntax is:

class DerivedClass : public BaseClass {
    // Additional members
};

The colon (:) introduces the inheritance list. public specifies the inheritance mode.

First working example


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

class Person {
public:
    string name;

    void introduce() {
        cout << "My name is " << name << '\n';
    }
};

class Student : public Person {
public:
    int studentId;

    void study() {
        cout << name << " is studying.\n";
    }
};

int main() {
    Student s;

    s.name = "Aarav";
    s.studentId = 101;

    s.introduce();  // Inherited from Person
    s.study();      // Defined in Student

    cout << "Student ID: " << s.studentId << '\n';

    return 0;
}

Output

My name is Aarav
Aarav is studying.
Student ID: 101

How it works

  1. Person declares name and introduce().

  2. Student : public Person declares Student as a derived class.

  3. A Student object can access the public members inherited from Person.

  4. Student adds its own member, studentId, and its own function, study().

Notice that Student does not redefine name or introduce(). That is the benefit of inheritance.

3. Types of inheritance

C++ supports several inheritance structures. The type depends on how the classes are connected.

Most Important Inheritance In C++ 2024 Guide

1. Single inheritance

One derived class inherits from one base class.

Example: Student inherits from Person.

Multilevel Inheritance In C++ | Syntax, Uses & More (+Codes) // Unstop

2. Multilevel inheritance

A class derives from another derived class.

Example: Person → Student → GraduateStudent.

Inheritance in C++ Simplified for Programmers

3. Multiple inheritance

One derived class inherits from two or more base classes.

Example: SmartDevice inherits from Camera and Phone.

UML Class Diagram Examples of Common Scenarios | EdrawMax

4. Hierarchical inheritance

Multiple derived classes inherit from the same base class.

Example: Student and Teacher both inherit from Person.

Learn to Solve the Diamond Problem in C++ | EDUCBA

5. Hybrid inheritance

A combination of inheritance structures, such as multilevel and multiple inheritance. Diamond-shaped hierarchies may require virtual inheritance to avoid duplicate base-class subobjects.

Example: multilevel inheritance


#include <iostream>
using namespace std;

class Person {
public:
    void eat() {
        cout << "Person can eat.\n";
    }
};

class Student : public Person {
public:
    void study() {
        cout << "Student can study.\n";
    }
};

class GraduateStudent : public Student {
public:
    void research() {
        cout << "Graduate student can research.\n";
    }
};

int main() {
    GraduateStudent g;

    g.eat();
    g.study();
    g.research();

    return 0;
}

Output

Person can eat.
Student can study.
Graduate student can research.

GraduateStudent inherits accessible members through Student from Person, so one object can use all three functions.

4. Public, protected, and private inheritance

One of the most important concepts in C++ inheritance is the inheritance access mode.

There are two separate ideas:

  1. A member's own access specifier: public, protected, or private.

  2. The inheritance mode written after the colon: public, protected, or private.

They work together to determine how inherited members can be accessed through the derived class.

Access conversion table

For members that are accessible to the derived class in the first place:

Base-class member

public inheritance

protected inheritance

private inheritance

public

public

protected

private

protected

protected

protected

private

private

Not directly accessible

Not directly accessible

Not directly accessible

A base class's private members still belong to its base-class subobject, but derived-class code cannot access them by name directly. Public or protected base-class functions may provide controlled access to that state.

Example: protected members


#include <iostream>
using namespace std;

class Employee {
protected:
    double salary = 50000;

public:
    void showSalary() {
        cout << "Salary: " << salary << '\n';
    }
};

class Manager : public Employee {
public:
    void giveRaise(double amount) {
        salary += amount;  // Allowed: salary is protected
    }
};

int main() {
    Manager m;

    m.giveRaise(5000);
    m.showSalary();

    // m.salary = 90000; // Error: protected member
    return 0;
}

Output

Salary: 55000

Manager can access salary inside its member function because it is derived from Employee. However, ordinary external code cannot use m.salary directly.

Practical rule: use private for internal implementation details and protected only when derived classes genuinely need direct access. Making everything protected can weaken encapsulation.

What if you omit the inheritance mode?

For a class, inheritance is private by default:

class Student : Person {
};

This is equivalent to:

class Student : private Person {
};

For a struct, inheritance is public by default:

struct Student : Person {
};

For readability, explicitly write the intended inheritance mode.

5. Constructors and destructors in inheritance

Constructors initialize objects, and inheritance affects the order in which initialization happens.

Construction order:

  1. Base-class constructor.

  2. Derived-class constructor.

Destruction order:

  1. Derived-class destructor.

  2. Base-class destructor.

Here is a complete example:


#include <iostream>
using namespace std;

class Base {
public:
    Base() {
        cout << "Base constructor\n";
    }

    ~Base() {
        cout << "Base destructor\n";
    }
};

class Derived : public Base {
public:
    Derived() {
        cout << "Derived constructor\n";
    }

    ~Derived() {
        cout << "Derived destructor\n";
    }
};

int main() {
    Derived obj;
    cout << "Inside main\n";

    return 0;
}

Output

Base constructor
Derived constructor
Inside main
Derived destructor
Base destructor

The base portion must be initialized before the derived portion. During destruction, the derived portion is cleaned up before the base portion.

Passing arguments to a base constructor

Use the derived class's member initializer list to select the appropriate base-class constructor.


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

class Person {
public:
    string name;

    Person(string n) : name(n) {
        cout << "Person initialized\n";
    }
};

class Student : public Person {
public:
    int id;

    Student(string n, int studentId)
        : Person(n), id(studentId) {
        cout << "Student initialized\n";
    }
};

int main() {
    Student s("Mira", 205);

    cout << s.name << '\n';
    cout << s.id << '\n';

    return 0;
}

Output

Person initialized
Student initialized
Mira
205

The expression Person(n) calls the base-class constructor with the name. The id(studentId) initializer initializes the derived class's own member.

If the base class has no accessible default constructor, the derived class must initialize the base using an appropriate constructor.

6. Function overriding

Sometimes a derived class needs to provide a specialized implementation of a function inherited from its base class.

This is called function overriding when a derived-class function overrides a compatible virtual function from the base class.


#include <iostream>
using namespace std;

class Animal {
public:
    virtual void sound() {
        cout << "Animal makes a sound.\n";
    }
};

class Dog : public Animal {
public:
    void sound() override {
        cout << "Dog barks.\n";
    }
};

int main() {
    Dog d;
    d.sound();

    return 0;
}

Output

Dog barks.

Two keywords matter here:

  • virtual in the base class enables dynamic dispatch for the function.

  • override tells the compiler that the derived function is intended to override a base-class virtual function. If it does not, compilation fails.

Overriding versus overloading

Overriding

Overloading

Redefines an inherited virtual function in a derived class

Defines multiple functions with the same name but different parameter lists

Requires a compatible base-class virtual function for virtual overriding

Can happen in one class or across scopes

Supports runtime polymorphism through virtual dispatch

Usually resolved using the argument types at compile time

Changing only the return type does not create a valid overload. Likewise, a derived function with a mismatched signature may hide a base function instead of overriding it.

7. Base-class pointers and runtime polymorphism

Inheritance becomes especially powerful when a base-class pointer or reference can refer to a derived-class object.

Consider:

Animal* ptr = &dog;
ptr->sound();

If sound() is virtual and dog is a Dog, the call uses the Dog implementation at runtime.

Here is a complete example:


#include <iostream>
using namespace std;

class Animal {
public:
    virtual void sound() {
        cout << "Generic animal sound\n";
    }

    virtual ~Animal() = default;
};

class Cat : public Animal {
public:
    void sound() override {
        cout << "Meow\n";
    }
};

int main() {
    Cat cat;
    Animal* ptr = &cat;

    ptr->sound();

    return 0;
}

Output

Meow

Because sound() is virtual, C++ selects the implementation for the actual object (Cat), not merely the pointer's declared type (Animal*).

Why make a base destructor virtual?

When an object may be deleted through a base-class pointer, the base class generally needs a virtual destructor.

Animal* ptr = new Cat();
delete ptr;  // Safe polymorphic destruction when Animal has a virtual destructor

Without a virtual base destructor, deleting a derived object through a base-class pointer causes undefined behavior in this situation.

If you use a base-class pointer merely to refer to an existing stack object, as in the previous example, do not delete that pointer.

8. Common inheritance mistakes

Mistake 1: Accessing private base members directly

A derived class cannot directly access a base class's private data members. Use an appropriate public or protected base-class method instead.

Mistake 2: Forgetting to initialize a base class

If the base class has no usable default constructor, initialize it explicitly in the derived class's initializer list.

Mistake 3: Expecting runtime dispatch without virtual

A call through a base pointer or reference does not automatically select the derived implementation. The relevant base function must be virtual.

Mistake 4: Confusing inheritance with containment

Use inheritance for a genuine “is-a” relationship, such as Student is a Person. Use composition for a “has-a” relationship, such as Car has an Engine.

9. Mini-project: Employee Management System

Let’s combine inheritance, constructors, protected members, and function overriding in a small program.

Requirements

  • Create a base class Employee.

  • Store the employee's name and monthly salary.

  • Derive Manager from Employee.

  • Add a bonus for the manager.

  • Display the total monthly pay using an overridden function.


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

class Employee {
protected:
    string name;
    double salary;

public:
    Employee(string employeeName, double monthlySalary)
        : name(employeeName), salary(monthlySalary) {}

    virtual double calculatePay() const {
        return salary;
    }

    virtual void display() const {
        cout << "Employee: " << name << '\n';
        cout << "Monthly pay: " << calculatePay() << '\n';
    }

    virtual ~Employee() = default;
};

class Manager : public Employee {
private:
    double bonus;

public:
    Manager(string managerName, double monthlySalary, double managerBonus)
        : Employee(managerName, monthlySalary),
          bonus(managerBonus) {}

    double calculatePay() const override {
        return salary + bonus;
    }

    void display() const override {
        cout << "Manager: " << name << '\n';
        cout << "Base salary: " << salary << '\n';
        cout << "Bonus: " << bonus << '\n';
        cout << "Total monthly pay: " << calculatePay() << '\n';
    }
};

int main() {
    Employee e("Riya", 40000);
    Manager m("Kabir", 60000, 10000);

    e.display();
    cout << '\n';
    m.display();

    return 0;
}

Output

Employee: Riya
Monthly pay: 40000

Manager: Kabir
Base salary: 60000
Bonus: 10000
Total monthly pay: 70000

Project breakdown

  • Manager : public Employee establishes the inheritance relationship.

  • Employee(managerName, monthlySalary) initializes the base portion.

  • protected allows Manager to access name and salary, while outside code cannot access them directly.

  • calculatePay() is virtual, so Manager can override its behavior.

  • const after the function parameter list indicates that the function does not modify the object's ordinary member state.

  • The virtual destructor supports safe destruction through an Employee* when used polymorphically.

Extension challenge: Create a SalesEmployee class that adds commission to base salary. Override calculatePay() and test both derived types through Employee* pointers.

10. Hands-on exercises

Try each exercise before opening its solution. Compile your programs to verify your understanding.

Exercise 1 — Basic inheritance

Beginner

Exercise 1

Create a base class Device with a public function powerOn() that prints “Device is on.” Derive a class Laptop and call powerOn() using a Laptop object.

Reveal solution Copy

Exercise 2 — Multilevel inheritance

Beginner

Exercise 2

Create classes A, B, and C so that B inherits from A and C inherits from B. Give each class a public function and call all three functions using an object of C.

Reveal solution Copy

Exercise 3 — Constructor order

Intermediate

Exercise 3

A base constructor prints “Base” and a derived constructor prints “Derived”. What is the output when you create one derived object?

Reveal solution Copy

Exercise 4 — Override a virtual function

Intermediate

Exercise 4

Create a base class Shape with a virtual function draw() and a derived class Circle that overrides draw(). Call draw() through a Shape pointer pointing to a Circle object.

Reveal solution Copy

11. Module summary

Keep these core rules in mind:

  • Inheritance enables reuse and specialization of class functionality.

  • class Derived : public Base declares public inheritance.

  • protected members are accessible inside the class and derived-class implementations, but not through ordinary external object access.

  • The base constructor runs before the derived constructor; destruction occurs in the reverse order.

  • A derived class can override a base-class virtual function.

  • Use override to catch mismatched overriding signatures.

  • Virtual functions enable runtime polymorphism through base-class pointers and references.

  • A polymorphic base class should generally have a virtual destructor when derived objects may be deleted through base pointers.

12. Interactive quiz — Test your understanding

Answer these eight questions to check your understanding of C++ inheritance. The quiz includes multiple-choice, select-all-that-apply, and true/false questions.

Given class Dog : public Animal {};, what is Animal in this declaration?

1 of 8

AA friend class

BA member object

CThe base class

DThe derived class

Next

13. Completion checklist

Module 12 progress

0/8

I can define base and derived classes.

I can write inheritance declarations using public, protected, and private modes.

I can identify single, multilevel, multiple, hierarchical, and hybrid inheritance.

I understand inherited-member accessibility.

I can explain constructor and destructor order.

I can implement a virtual function and override it.

I understand base pointers and runtime polymorphism.

I completed the exercises and reviewed my quiz results.