SrcForge

Lesson 30

Inheritance

Covers inheritance syntax, access modes, types of inheritance, protected members, and constructor/destructor execution order.

Lesson content

What is inheritance?

Inheritance is a mechanism that allows one class (called the derived class) to acquire the data members and member functions of another class (called the base class).

  • Base class (parent class): the class whose members are inherited
  • Derived class (child class): the class that inherits from the base class

Think of a parent and child relationship. A child inherits traits such as eye color or height from the parent, but can also have its own unique characteristics. Similarly, a derived class inherits the features of the base class while adding its own functionality.

Why does inheritance exist?

  • Reusing existing code
  • Reducing redundancy
  • Making programs easier to extend
  • Representing real-world relationships

Real-world use cases

  • Banking systems
  • Employee management systems
  • Game development
  • GUI frameworks
  • Vehicle management systems
  • Educational software
  • E-commerce applications

Prerequisites

  • Classes and objects
  • Constructors and destructors
  • Access specifiers (public, private, protected)
  • Member functions
  • Basic Object-Oriented Programming concepts

Basic syntax

class BaseClass
{
    // Base class members
};

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

class Animal
{
public:
    void eat()
    {
        cout << "Animal is eating." << endl;
    }
};

class Dog : public Animal
{
public:
    void bark()
    {
        cout << "Dog is barking." << endl;
    }
};
  • Animal is the base class
  • Dog is the derived class
  • Dog inherits the eat() function

Access modes in inheritance

The inheritance mode determines how base class members are inherited.

  • public inheritance: public members remain public, protected members remain protected, private members not accessible
  • protected inheritance: public members become protected, protected members remain protected, private members not accessible
  • private inheritance: public members become private, protected members become private, private members not accessible

Types of inheritance

  • Single inheritance: one derived class inherits from one base class
  • Multiple inheritance: a class inherits from more than one base class
  • Multilevel inheritance: a derived class becomes the base class for another class
  • Hierarchical inheritance: multiple derived classes inherit from one base class
  • Hybrid inheritance: a combination of two or more inheritance types, which may introduce the diamond problem, commonly solved using virtual inheritance

Protected members

Protected members are accessible within the class and its derived classes.

class Animal
{
protected:
    int age;
};

class Dog : public Animal
{
public:
    void setAge(int value)
    {
        age = value;
    }
};

Constructor execution order

When an object of a derived class is created, the base class constructor executes first, then the derived class constructor executes.

When the object is destroyed, the derived class destructor executes first, then the base class destructor executes.

Example 1: Single inheritance

#include <iostream>
using namespace std;

class Animal
{
public:

    void eat()
    {
        cout << "Animal is eating." << endl;
    }
};

class Dog : public Animal
{
public:

    void bark()
    {
        cout << "Dog is barking." << endl;
    }
};

int main()
{
    Dog dog;

    dog.eat();

    dog.bark();

    return 0;
}

// Output:
// Animal is eating.
// Dog is barking.

Example 2: Multilevel inheritance

#include <iostream>
using namespace std;

class Animal
{
public:

    void eat()
    {
        cout << "Animal eats." << endl;
    }
};

class Mammal : public Animal
{
public:

    void walk()
    {
        cout << "Mammal walks." << endl;
    }
};

class Dog : public Mammal
{
public:

    void bark()
    {
        cout << "Dog barks." << endl;
    }
};

int main()
{
    Dog dog;

    dog.eat();

    dog.walk();

    dog.bark();

    return 0;
}

Example 3: Multiple inheritance

#include <iostream>
using namespace std;

class Printer
{
public:

    void print()
    {
        cout << "Printing..." << endl;
    }
};

class Scanner
{
public:

    void scan()
    {
        cout << "Scanning..." << endl;
    }
};

class AllInOne : public Printer, public Scanner
{
};

int main()
{
    AllInOne device;

    device.print();

    device.scan();

    return 0;
}

Example 4: Protected members

#include <iostream>
using namespace std;

class Employee
{
protected:

    double salary;

public:

    Employee()
    {
        salary = 50000;
    }
};

class Manager : public Employee
{
public:

    void displaySalary()
    {
        cout << "Salary: " << salary << endl;
    }
};

int main()
{
    Manager manager;

    manager.displaySalary();

    return 0;
}

// Output: Salary: 50000

Example 5: Constructor order

#include <iostream>
using namespace std;

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;
    }
};

int main()
{
    Derived object;

    return 0;
}

// Output:
// Base Constructor
// Derived Constructor
// Derived Destructor
// Base Destructor

Common mistakes and pitfalls

  • Trying to access private base class members directly. Fix: use public or protected member functions to access them
  • Using private inheritance when public inheritance represents an "is-a" relationship. Fix: use public inheritance for true inheritance relationships
  • Forgetting to call the appropriate base class constructor. Fix: use constructor initializer lists when needed
  • Overusing inheritance for code reuse. Fix: consider composition if an "is-a" relationship doesn't exist
  • Ignoring virtual destructors in polymorphic base classes. Fix: declare the base destructor as virtual when deleting derived objects through base pointers

Best practices

  • Use public inheritance only for true "is-a" relationships
  • Prefer composition over inheritance when appropriate
  • Keep base classes focused and reusable
  • Make destructors virtual in polymorphic base classes
  • Avoid deep inheritance hierarchies that are difficult to maintain
  • Use protected members sparingly; prefer private members with protected or public interfaces when appropriate

When not to use this

  • The relationship is "has-a" rather than "is-a"
  • You only want to reuse code without modeling a true inheritance relationship
  • Composition provides a simpler and more flexible design
  • The inheritance hierarchy becomes overly complex

Summary

  • Inheritance allows one class to inherit members from another
  • The base class provides common functionality
  • The derived class extends or specializes the base class
  • C++ supports single, multiple, multilevel, hierarchical, and hybrid inheritance
  • Public inheritance models an "is-a" relationship
  • Base constructors execute before derived constructors
  • Derived destructors execute before base destructors
  • Use inheritance thoughtfully to improve code reuse and maintainability

Frequently asked questions

What is inheritance in C++?

Inheritance is an Object-Oriented Programming feature that allows a derived class to reuse and extend the members of a base class.

What is the difference between a base class and a derived class?

A base class is the parent class that provides common members. A derived class is the child class that inherits and extends those members.

What are the different types of inheritance in C++?

C++ supports single inheritance, multiple inheritance, multilevel inheritance, hierarchical inheritance, and hybrid inheritance.

What is the difference between public, protected, and private inheritance?

They determine how inherited members are accessed in the derived class. Public inheritance is the most common because it preserves the public interface of the base class.

When should I use inheritance instead of composition?

Use inheritance when there is a genuine "is-a" relationship, such as a Car is a Vehicle. Use composition when there is a "has-a" relationship, such as a Car has an Engine.