Object-Oriented Programming (C++)— Inheritance | The Easy Way (Part III)

Object-Oriented Programming (C++)— Inheritance | The Easy Way (Part III)

What’s up fellas? This what you see is the third part of my Object-Oriented Programming concepts series. I would be covering the concept of Inheritance today which is one of the pillars of OOP Concepts.

Let’s get going!

Starting from the basics, what Inheritance means and why in the world do we want to inherit?

Well, if we talk in terms of Classes, Inheritance simply means deriving a new class from an old one. The old class is referred to as the Base Class and the new one is called Derived Class or Subclass.

We need inheritance because no one wants to redo/reimplement a function that has already been implemented! I mean who would want that?

Reusability is one of the most important features of Inheritance. It is not always about doing extra work but the already done work might have also been tested well for any bugs and you obviously don’t want to go through that painful cycle again! Hence, just Reuse.

- Forms of Inheritance

A parent class can be inherited by a child class in one of the following forms:

1) Single Inheritance 2) Multiple Inheritance 3) Hierarchical Inheritance 4) Multilevel Inheritance 5) Hybrid Inheritance

The below diagram will help you understand these forms in a nutshell.

1_K2wL19wvlNUcvv70cJqpnQ.png

Diving deep inside inheritance, it becomes important for us to understand which components within a base class can be inherited and used by the child class because a base class can contain multiple types of components like private, protected, and public. Let’s try to work this out with the help of a simple program, inherited from my mind! :P

#include<bits/stdc++.h>
using namespace std;

class Person{
    private:
        string name;
        int age;

    public:
        Person(string name, int age): name{name}, age{age}{
            //Base parameterized constructor initialised using memeber initializer list
        }
        void getName(){
            cout<<"Name: "<<name<<endl;
        }
        void getAge(){
            cout<<"Age: "<<age<<endl;
        }
};

class Employee: private Person{ //Default inheritance type is private
    private:
        int employeeID;
    public:
        Employee(string name, int age, int id) : Person(name, age){  //Derived parameterized constructor
            employeeID = id;
        }
        void getEmployeeDetails(){
            getName();
            getAge();
            cout<<"Employee ID: "<<employeeID<<endl;
        }
};

int main(){
    Employee* e = new Employee("John", 24, 14298);
    e->getEmployeeDetails();
    return 0;
}

/*
Output:
Name: John
Age: 24
Employee ID: 14298
*/

One important thing to note in the above code is that the Person class has been inherited privately. The default form of inheritance in private, hence we could have skipped writing private. Moreover, with this type of inheritance, all the members of class Person now become only privately accessible members (except name & age, because they are too private) of the class Employee. If you are now having questions like what would happen if it was inherited publicly or in a protected way? We’ll be discussing these very soon! Hold back!

Here, we have used both base and derived parameterized constructors. The base derived constructor is executed first.

1_R4Ed_EKlTxcyG1XgX9ihFQ.png

Don’t be afraid of the virtual keyword. We will be discussing it soon! But before that, it is important to know that there are three types of access specifiers present in C++ to encapsulate the members of a class.

1) Private — Optional | Visible to member functions within the class only. 2) Protected — Visible to member functions of its own and derived class. 3) Public — Visible to all functions in the program

Different classes can be inherited in one of the above ways, and all its members are inherited differently based on how the base class is inherited. The below diagram will clear all your doubts about types of inheritance and accessible members of a class when inherited.

1_DT-wl517GgbelVFjkZvTTA.png

I hope we are on the same page till now! Moving on, if you hadn’t had asked yourself what would happen if two same name members are present in both the parent and the child class. Well, now is the time!

Let’s discuss the ambiguity resolution in inheritance. For an easy understanding of the concept, I have chosen a single inheritance example below:

- Ambiguity Resolution in Inheritance

#include<bits/stdc++.h>
using namespace std;

class A{
    public:
        void display(){
            cout<<"A"<<endl;
        }
        void showInfo(){
            cout<<"I am parent, A!"<<endl;
        }
};

class B: public A{
    public:
        void display(){
            A::showInfo();
            cout<<"B"<<endl;
        }
};

int main(){
    B b;
    b.display();
    cout<<"--------"<<endl;
    b.A::display();
    cout<<"--------"<<endl;
    b.B::display();
    return 0;
}
/*
Output
I am parent, A!
B
--------
A
--------
I am parent, A!
B
*/

Note that, we have used the scope resolution operator here (::) to call the parent class method.

Since we are already talking about Ambiguity resolution, let’s discuss a case where all three kinds of inheritance, i.e, multilevel, multiple, and hierarchical inheritances are involved.

1_hT8YcYoWsa0w1IUJbUCsQg.png

Here, class ‘D’ has two direct base classes, ‘B’ and ‘C’ which themselves have a common base class ‘A’.

Class ‘D’ inherits the properties of ‘A’ is sometimes referred to as an indirect base class.

This scenario here has a problem. All the protected and public members of class ‘A’ are inherited twice into ‘D’ from both ‘B’ and ‘C’. This leads to ambiguity as there are duplicate sets of the members of class ‘A’ inherited within class ‘D’.

The solution to this particular problem is making the common base class (ancestor class) as a Virtual Base Class while declaring the direct or indirect base classes.

1_1E717NDwCjPu-1UkXP2fJg.png

When a class is made a virtual base class, C++ takes necessary care to see that only one copy of that class is inherited, regardless of how many inheritance paths exist between the virtual base class and a derived class.

Let us try to understand this with the help of code because that’s how we really get the feel! ;)

#include<bits/stdc++.h>
using namespace std;

class Student{
    private:
        string name;
        int id;

    public:
        void setStudentDetails(string n, int num){
            name = n;
            id = num;
        }
        void getStudentName(){
            cout<<"Student Name: "<<name<<endl;
        }
        void getStudentID(){
            cout<<"Student ID: "<<id<<endl;
        }
};

class Theory: public virtual Student{
    public:
        string subjectName;
        int score;

        void setTheoryScores(string subName, int marks){
            subjectName = subName;
            score = marks;
        }
        void getTheoryResults(){
            cout<<"----------Theory---------"<<endl;
            getStudentName();
            cout<<"Subject Name: "<<subjectName<<" => Marks: "<<score<<endl;
        }
};

class Practical: virtual public Student{
    public:
        string subjectName;
        int score;

        void setPracticalScores(string subName, int marks){
            subjectName = subName;
            score = marks;
        }
        void getPracticalResults(){
            cout<<"----------Practical---------"<<endl;
            getStudentName();
            cout<<"Subject Name: "<<subjectName<<" => Marks: "<<score<<endl;
        }
};

class ReportCard: public Theory, public Practical{
    private:
        int totalMarks;

    public:
        void setTotalMarks(){
            totalMarks = Theory::score + Practical::score;
        }
        void getResult(){
            getStudentName();
            getStudentID();
            getTheoryResults();
            getPracticalResults();
        }
};

int main(){
    ReportCard r;
    r.setStudentDetails("John Wick", 12428);
    r.setTheoryScores("Chemistry", 98);
    r.setPracticalScores("Chem Lab", 96);
    r.setTotalMarks();
    r.getResult();

    return 0;
}
/*
Output: 
Student Name: John Wick
Student ID: 12428
----------Theory---------
Student Name: John Wick
Subject Name: Chemistry => Marks: 98
----------Practical---------
Student Name: John Wick
Subject Name: Chem Lab => Marks: 96
*/

In the above code, we have to include only one copy of the class Student’s members within the class ReportCard with the help of the virtual base class concept. Feel free to explore this on your own!

Moving forward, there’s this one last concept that we’ll be touching, Abstract Classes.

Abstract classes are nothing but C++ interfaces that describe the behavior or capabilities of a class without committing to a particular implementation of that class. A class is made abstract by declaring at least one of its functions as a pure virtual function. A pure virtual function is specified by placing a “=0” in its declaration.

The purpose of an abstract class is to provide an appropriate base class from which other classes can inherit. Abstract classes cannot be used to instantiate objects and serve only as an interface. Attempting to instantiate an object of an abstract class causes a compilation error.

Let’s try to understand this with the help of an example:

#include<bits/stdc++.h>
using namespace std;

class Shape{
    protected:
        int width;
        int length;

    public:
        virtual void getArea() = 0;
        void setWidth(int w){
            width = w;
        }
        void setLength(int l){
            length = l;
        }
};

class Square: public Shape{
    public:
        void getArea(){
            cout<<(width*length)<<endl;
        }
};

class Triangle: public Shape{
    public:
        void getArea(){
            cout<<((width * length) / 2)<<endl;
        }
};

int main(){

    Square sq;
    sq.setLength(4);
    sq.setWidth(4);
    sq.getArea();

    Triangle t;
    t.setLength(10);
    t.setWidth(5);
    t.getArea();

    return 0;
}
/*
Output
16
25
*/

Note that the getArea() method within the base class Shape is a pure virtual function and thus the class Shape is an abstract class.

This my friend, marks the end of our discussion regarding Inheritance.

I tried to cover a lot about Inheritance within a single article so that we don’t waste our time searching for different resources!

About me? I am a tech lover! I love to read, implement and explain :)

Best.