1. Home
C++ Tutorial

Explore C++ Tutorials: Exploring the World of C++ Programming

Discover comprehensive C++ tutorials designed for beginners and advanced programmers alike. Enhance your coding skills with step-by-step guides and practical examples.

  • 77 Lessons
  • 15 Hours
right-top-arrow
25

Hierarchical Inheritance in C++: Concepts, Syntax & Examples

Updated on 26/09/2024418 Views

Hierarchical inheritance is a fundamental concept in object-oriented programming (OOP) where multiple classes inherit properties and behaviors from a single base class. We can think of it as a family tree, where the base class represents a grandparent, and various derived classes represent children and grandchildren, each inheriting traits from their ancestors.

Let us dive in and all about hierarchical inheritance in C++.

What is Hierarchical Inheritance?

In order to learn what is hierarchical inheritance in C++, we must first discuss what single inheritance and multiple inheritance is. When multiple classes inherit from a common base class, we refer to it as hierarchical inheritance (i.e., Dog, Cat, and Horse all inheriting from Animal). Being able to build hierarchical class structures is extremely crucial as a C++ developer as we need it for many different programs.

Imagine a digital family tree where the grandparent class, Animal, represents the shared characteristics of all animals. From this root, various derived classes emerge: Dog, Cat, Horse, each inheriting traits like age, name, and the ability to eat(). However, each class also boasts unique qualities: Dog might bark(), Cat could meow(), and Horse might gallop().

Advantages of Hierarchical Inheritance in C++

Here are the advantages of hierarchical inheritance in C++:

  • Code reusability: The base class provides a foundation of common attributes and methods, eliminating the need to rewrite code in each derived class.
  • Maintainability: Changes to the base class automatically propagate to all derived classes, simplifying updates and bug fixes.
  • Real-world modeling: Hierarchical inheritance in C++ mirrors real-world relationships, making code more intuitive and easier to understand. For instance, different types of animals share common traits (inherited from the Animal class) but also have unique characteristics.

Core Concepts of Hierarchical Inheritance in C++

Let us first learn the core concepts of hierarchical inheritance such as class hierarchy in C++ and the syntax.

Base Class (The Ancestor)

At the heart of hierarchical inheritance in C++ lies the base class, also known as the parent or superclass. It serves as the blueprint for its descendants, holding common properties and behaviors that all derived classes will share. In our animal kingdom analogy, Animal would be the base class, defining shared traits like age, name, and the eat() function.

Derived Classes (The Descendants)

Derived classes, also known as child or subclasses, are the offspring of the base class. They inherit the attributes and methods of their parent while adding their unique characteristics. For example, Dog, Cat, and Horse are derived classes of Animal, each inheriting common traits but also having their own special abilities like barking, meowing, or galloping.

Syntax of Hierarchical Inheritance in C++

In C++, we establish this parent-child relationship using this syntax:

class DerivedClass : public BaseClass {

// Additional members specific to the DerivedClass

};

In the above code snippet:

  • class DerivedClass: This declares the new class we're creating.
  • : public BaseClass: This indicates that DerivedClass inherits from BaseClass. The keyword public signifies public inheritance, the most common type. We will explore other inheritance types (protected and private) later.
  • // Additional members ...: This is where you add any unique attributes or methods that are specific to the DerivedClass.

The Family Tree of Inheritance

The family tree analogy helps visualize the flow of inheritance and the relationships between classes. It also makes it easier to understand how hierarchical inheritance promotes code reusability and maintainability while modeling real-world scenarios. Imagine a family tree where each generation represents a class in our code:

Grandparents (Base Class)

|

Parents (Derived Class 1)

/ \

Child 1 (Derived Class 2) Child 2 (Derived Class 2)

How traits pass down:

  • Grandparents (base class): They possess foundational traits like eye color, hair type, or a knack for storytelling.
  • Parents (derived class 1): They inherit these traits from their parents while adding their own unique characteristics like a love of music or a talent for cooking.
  • Children (derived class 2): They inherit traits from both their parents and grandparents, creating a blend of family history and individuality. Some may even develop entirely new talents.

Connecting the Analogy to Code

In our C++ code, the analogy translates beautifully:

  • Base class: Like grandparents, the base class defines the fundamental properties and behaviors (member variables and functions) that are shared by all its descendants.
  • Derived classes (level 1): Like parents, these classes inherit the traits of the base class and extend them with additional members specific to their own identity.
  • Derived classes (level 2): Like children, these classes inherit traits from both their parent class and the grandparent (base) class, forming a multi-generational hierarchy.

Example:

Shape (Base Class)

|

Polygon (Derived Class 1)

/ \

Triangle (Derived Class 2) Rectangle (Derived Class 2)

In the above example:

  • Shape (base class): Defines basic properties like color and area.
  • Polygon (derived class 1): Inherits from Shape and adds properties like numSides.
  • Triangle, Rectangle (derived class 2): Inherit from Polygon and further specialize with unique properties and methods (e.g., base, height for Triangle, width, length for Rectangle).

Simple Hierarchical Inheritance in C++ Example

Let us lay the foundation for creating a rich and diverse animal kingdom within our C++ code with a simple structure.

Code:

#include <iostream>

#include <string>

using namespace std;

class Animal {

public:

int age;

string name;

Animal(int age, string name) : age(age), name(name) {} // Constructor to initialize

void eat() {

cout << name << " is eating." << endl;

}

};

class Dog : public Animal {

public:

Dog(int age, string name) : Animal(age, name) {} // Call base class constructor

void bark() {

cout << name << " is barking." << endl;

}

};

class Cat : public Animal {

public:

Cat(int age, string name) : Animal(age, name) {} // Call base class constructor

void meow() {

cout << name << " is meowing." << endl;

}

};

int main() {

Dog myDog(3, "Buddy");

Cat myCat(2, "Whiskers");

myDog.eat(); // Output: Buddy is eating.

myDog.bark(); // Output: Buddy is barking.

myCat.eat(); // Output: Whiskers is eating.

myCat.meow(); // Output: Whiskers is meowing.

return 0;

}

In this hierarchical inheritance in C++ program, we define the Animal class with common attributes (age, name) and a common behavior (eat()). Dog and Cat inherit from Animal, gaining access to age, name, and the ability to eat(). Finally, Dog adds its unique bark() method, while Cat adds its unique meow() method.

If you wish to learn how to code in C++, you can check out upGrad’s software engineering courses.

The Order of Construction and Destruction

When it comes to hierarchy inheritance in C++, the creation and destruction of objects follow a specific order, much like a chain reaction. This strict order is crucial to ensure that objects are constructed and destroyed in a consistent and predictable way.

It guarantees that the derived class constructor can rely on the base class having already been constructed, allowing it to safely use inherited members. It also ensures that during destruction, the derived class can clean up its own resources before the base class releases the underlying memory.

  1. Base class constructor: The constructor of the base class is called first. This is essential because the base class lays the foundation for the object, setting up its initial state and memory allocation.
  1. Derived class constructor(s): Next, the constructor of the derived class is called. This constructor can further initialize any additional members specific to the derived class. The derived class constructor may also call the base class constructor using a member initializer list to set up the inherited members properly.
  1. Object creation: Once both constructors have run, the object is considered fully constructed and ready for use.

The destruction process reverses this order:

  1. Derived class destructor: The destructor of the derived class is called first to clean up any resources it allocated.
  1. Base class destructor: Finally, the destructor of the base class is called to release any memory or resources it holds.

Example: The Animal Hierarchy (Revisited)

Let's revisit our Animal, Dog, and Cat example to see constructors and destructors in action:

class Animal {

public:

Animal(int age, string name) { // Base constructor

this->age = age;

this->name = name;

cout << "Animal constructor called for " << name << endl;

}

~Animal() { // Base destructor

cout << "Animal destructor called for " << name << endl;

}

// ... rest of the class

};

//Similar constructors and destructors for Dog and Cat...

Visibility Modes: Controlling Access to Inherited Members

In C++, the way a derived class inherits from a base class determines how accessible the base class's members (variables and functions) are within the derived class and to the outside world. These visibility modes are controlled by the keywords public, protected, and private used in the inheritance syntax.

Public Inheritance (Default & Most Common)

This is the most intuitive and commonly used inheritance mode. It allows the derived class to extend the base class's functionality while maintaining the same level of access control.

Inheritance syntax: class DerivedClass : public BaseClass { ... };

Access rules:

  • Public members of the base class remain public in the derived class.
  • Protected members of the base class remain protected in the derived class.
  • Private members of the base class are inaccessible to the derived class (but they are still inherited).

Protected Inheritance (Limited Access)

Protected inheritance restricts the access of public members to the derived class and any further derived classes (grandchildren, etc.). It's useful when you want to expose certain members only to descendants while hiding them from external code.

Inheritance syntax: class DerivedClass : protected BaseClass { ... };

Access rules:

  • Public members of the base class become protected in the derived class.
  • Protected members of the base class remain protected in the derived class.
  • Private members of the base class are inaccessible to the derived class.

Private Inheritance (Implementation Detail)

Private inheritance treats the base class as an implementation detail of the derived class. Inherited members are essentially hidden within the derived class and cannot be accessed directly by its objects or any further derived classes. This is often used when you want to reuse code from the base class without exposing its interface to the outside world.

Inheritance syntax: class DerivedClass : private BaseClass { ... };

Access rules:

  • Public and protected members of the base class become private in the derived class.
  • Private members of the base class are inaccessible to the derived class.

Hierarchical Inheritance in C++ Overriding: Adapting Behavior

In hierarchical inheritance, derived classes often need to modify or specialize the behavior of methods inherited from the base class. This is where overriding comes in. Overriding allows a derived class to provide its own implementation for a function that is already defined in its base class.

Virtual Functions and Polymorphism

To enable overriding, you must declare the base class function as virtual. A virtual function acts as a placeholder, signaling to the compiler that derived classes might provide their own specific implementations. This is a crucial ingredient for polymorphism, the ability of objects of different classes to be treated as objects of a common base class.

The override keyword (introduced in C++11) is a specifier used in the derived class's function declaration. It serves two purposes:

  • Explicit intent: It makes it clear that you intend to override the base class's virtual function.
  • Compiler check: It helps the compiler catch errors if you accidentally misspell the function name or change its parameters, which would lead to unexpected behavior.

Function overriding in hierarchical inheritance real time example:

Code:

#include <iostream>

#include <cmath> // For M_PI

using namespace std;

class Shape {

public:

virtual double area() const { return 0.0; }

virtual double perimeter() const { return 0.0; }

};

class Rectangle : public Shape {

public:

Rectangle(double width, double height) : width(width), height(height) {}

double area() const override { return width * height; }

double perimeter() const override { return 2 * (width + height); }

private:

double width;

double height;

};

class Circle : public Shape {

public:

Circle(double radius) : radius(radius) {}

double area() const override { return M_PI * radius * radius; }

double perimeter() const override { return 2 * M_PI * radius; }

private:

double radius;

};

int main() {

Shape* shapes[] = {new Rectangle(4, 5), new Circle(3) };

for (Shape* s : shapes) {

cout << "Area: " << s->area() << ", Perimeter: " << s->perimeter() << endl;

}

// Release memory (crucial for objects created with 'new')

for (Shape* s : shapes) {

delete s;

}

return 0;

}

You can try out the above hierarchical inheritance in C++ example yourself to experiment with override. In this example, the base class Shape declares area() and perimeter() as virtual functions. The default implementation returns 0.0. Rectangle overrides these functions with its own specific calculations.

The main function creates an array of Shape pointers, which can point to objects of any derived class (Rectangle, Circle, etc.). When area() or perimeter() is called on a Shape pointer, the correct version of the function is dynamically determined at runtime based on the actual object type (polymorphism).

Final Tips

Hierarchical inheritance in C++ is not just a theoretical concept, it is a practical skill that can elevate your C++ programming. The best way to solidify your understanding is to put it into practice.

Also, we should choose wisely between public, protected, and private inheritance to control access to inherited members and tailor your class relationships. Finally, always remember the strict order of their execution to ensure proper object initialization and cleanup.

If you wish to learn programming languages such as C++, you can check out upGrad’s computer science programs such as the Master’s in Computer Science Program.

Frequently Asked Questions

  1. What is hierarchical inheritance in C++?

Hierarchical inheritance is an object-oriented concept where multiple derived classes inherit properties and behaviors from a single base class.

  1. How does hierarchical inheritance work?

Derived classes inherit the members (variables and functions) of the base class and can add their own unique members.

  1. What are the advantages of hierarchical inheritance?

It promotes code reusability, simplifies maintenance, and models real-world relationships effectively.

  1. How do you implement hierarchical inheritance in C++?

Use the syntax class DerivedClass : public BaseClass { ... };, specifying the access mode (public, protected, or private).

  1. What is the function of hierarchical inheritance?

It organizes classes into a tree-like structure, representing a "is-a" relationship, and promotes modular code design.

  1. Where is hierarchical structure used?

It's used in many software systems, like modeling biological classifications, organizing employee hierarchies, or representing geometric shapes.

Rohan Vats

Rohan Vats

Software Engineering Manager @ upGrad. Passionate about building large scale web apps with delightful experiences. In pursuit of transforming eng…Read More

Need Guidance? We're Here to Help!
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...