Inheritance In C++

Importance Of Inheritance In C++ With Examples:

Inheritance is one of the most important features of object-oriented programming.

Inheritance is the technique by which one class acquires the properties and methods of other class. This way we can reuse the code that is already written and verified. The class that acquires the properties of another class is called the subclass or derived class or child class.

The class whose properties are acquired is called the base class or parent class or superclass. When one class acquires or inherits another class, then all the properties and methods of the base class are available for the derived class, so that we can reuse this code.

=> Visit Here To Learn C++ From Scratch.

INHERITANCE IN C++

Why Do We Need Inheritance?

Consider a group of vehicles like car, bus, jeep, etc. Each of these vehicles will have properties and methods as indicated in the below diagram.

Inheritance Example

If we are required to implement individual classes for the above vehicles, we can see that in all the three classes, we will have to write the same code as all the three types of vehicles more or less exhibit the same properties. This will make our program inefficient and cumbersome as there will be a lot of duplicate code.

Instead of writing a duplicated code like above, we can implement the feature of inheritance to prevent the code from duplicating and also write a single piece of code and use it in all the three classes. This is pictorially represented as below.

feature of inheritance

In the above figure, we have defined a base class “Vehicles” and derived the classes Car, Bus and Jeep from this class. The common methods and properties are a part of the Vehicles class now. As other classes are derived from the Vehicles class, all the classes acquire these methods and properties.

Hence, we just need to write the common code only once and all three classes; Car, Bus and Jeep will acquire it.

Thus the main advantage, we get by inheriting the existing classes or designing inheritance mechanism is the reusability of code.

The general format for inheriting a class is:

class derived_classname: access_specifier base_classname
 {
 };

Here “derived_classname” is the name of the derived class, “access_specifier” is the access mode i.e. public, protected or private in which the derived class has to inherit the base class and “derived_classname” is the name of the base class from which the derived class inherits.

Modes Of Inheritance

The “access_specifier” shown in the above declaration of inheritance, can have their values as shown below.

Access specifiers

Depending on the access_specifier specified when we inherit the class, we have various modes of inheritance as listed below.

Public Inheritance

General syntax

class sub_class : public parent_class

When public access specifier is specified, the public members of the base class are inherited as public while protected members are protected. Private members remain private. This is the most popular mode of inheritance.

Private Inheritance

General Syntax

class sub_class : parent_class

Private inheritance does not inherit anything. When private access specifier is used, public and protected members of the base class also become private.

Protected Inheritance

General Syntax

class sub_class:protected parent_class

When protected access specifier is used, public and protected members of the base class become protected members in the derived class.

Note that when we use private access specifier for the base class, none of the base class members are inherited. They all become private in the derived class.

Given below is the tabularized representation of all the access modes and their interpretation for inheritance.

Derived Class-->

Base class
PrivatePublicProtected
PrivateNot inheritedNot InheritedNot Inherited
PublicPrivatePublicProtected
ProtectedPrivateProtectedProtected

Order Of Constructors/Destructors In Inheritance

When classes are inherited, the constructors are called in the same order as the classes are inherited. If we have a base class and one derived class that inherits this base class, then the base class constructor (whether default or parameterized) will be called first followed by the derived class constructor.

The following program demonstrates the order of constructors in inheritance. We have a Base class “Base” which has a default constructor and a parameterized constructor. We derive a class from this called “Derived” which also has one default and another parameterized constructor.

The output of this program shows the order in which the constructors are called.

#include <iostream>
using namespace std;
//order of execution of constructors in inheritance
class Base
{
   int x;
   public:
   // default constructor
   Base()
   {
      cout << "Base class default constructor\n"; } //parameterized constructor Base(int x) { this->x = x;
      cout<<"Base class parameterized constructor\n";
   }
};
class Derived : public Base
{
   int y;
   public:
   // default constructor
   Derived()
   {
      cout << "Derived class default constructor\n";
   }
   // parameterized constructor
   Derived(int i):Base(i)
   {
      cout << "Derived class parameterized constructor\n";
   }
};
int main()
{
   Base b; //construct base class object
   Derived d1; //construct derived class object with default constructor
   Derived d2(10); //construct derived class object with parameterized constructor
}

Output:

Base class default constructor
Base class default constructor
Derived class default constructor
Base class parameterized constructor
Derived class parameterized constructor

We see that after creating the base class object we create a derived class object with a default constructor. When this object is created, first the base class default constructor is called and then the derived class constructor is executed.

Similarly, when the derived class object is created using the parameterized constructor, the base class parameterized constructor is called first and then the derived class constructor is called.

Note that if there was no parameterized constructor in the base class, then the default constructor would have been called even for constructing the parameterized derived class object.

But the question remains as to why base class constructor is called while constructing the derived class objects?

We know that a constructor is used to create objects of the class and also to initialize the members of the class. When the derived class object is created, its constructor only has control over the derived class members.

However, the derived class also inherits the members of the base class. If only the derived class constructor was called, then the base class members inherited by the derived class would not be initialized properly.

As a result, the entire object will not be created efficiently. This is the reason for which all the base class constructors are called first when a derived class object is created.

Types Of Inheritance

Depending on the way in which the class is derived or how many base classes a class inherits, we have the following types of inheritance as depicted in the figure below.

Types of Inheritance

We will explore each of these types in our next tutorial on “Types of Inheritance”.

Template Inheritance

When our implementation involves templates, then we need to inherit or derive from template classes, and we make use of template inheritance there.

Let us directly jump to a programming Example to better understand the inheritance using templates.

#include <iostream>
using namespace std;
//template inhertance
template<typename T>class basecls_Template
{
   public:
   T value;
   basecls_Template(T value) {
      this->value = value;
}
void displayVal() {
   cout << value << endl;
   }
};
//derived class inherits basecls_Template<char>
class derivedcls_Child : public basecls_Template<char>
{
   public:
   derivedcls_Child(/* no parameters */): basecls_Template<char>( 0 ){ // default char is NULL;
}
derivedcls_Child(char c): basecls_Template<char>( c ) {
   ;
   }
void displayVal_drvd() {
   displayVal();
   }
};
int main()
{
   basecls_Template <int> obj( 100 );
   derivedcls_Child obj1( 'A' );
   cout<<"basecls_Template<int> obj = ";
   obj.displayVal(); // should print "100"
   cout<<endl;
 
cout<<"derivedcls_Child obj1(inherited from basecls_Template<char> = ";
obj1.displayVal_drvd();  // should print "A"
cout<<endl;
 
return 0;
}

Output:

basecls_Template<int> obj = 100

derivedcls_Child obj1(inherited from basecls_Template<char> = A

In the above program, we have a template named basecls_Template which defines the class template for the base class. Next, we define a class derivedcls_Child which we want to derive from a template class.

But note that the class basecls_Template is only a type and not a class. Hence, we cannot derive the class derivedcls_Child from this template.

Therefore if we declare the child class as:

class derivedcls_Child : public basecls_Template

This will result in an error. The reason being basecls_Template is a data type and not class. Thus in order to inherit the members of basecls_Template, we should first instantiate it before we derive from it.

Therefore the above statement, Class derivedcls_Child : public basecls_Template<char> works fine.

In this statement, we have instantiated the template basecls_Template to a character class template. Once we use this instantiated template class, then the other things following that like creating and using objects coincide with the usual inheritance working.

Composition

So far we have seen all about inheritance relationships. Inheritance basically depicts the kind of relationships wherein the relationship indicates a part. For Example, a Snake is a kind of a Reptile. We can also say Reptile is a part of Animal class.

In conclusion, inheritance indicates “IS-A” kind of relationships wherein we can say that the derived class is a part of the base class.

We can also represent relationships as a whole. For Example, if we say Salary class is a part of Employee class, then we are not representing it properly. We know that Employees has a salary. Thus it is more convenient to say “Employee has a salary”.

Similarly, if we take the Vehicles class as an example, we can say that Vehicle has Engine or Vehicle has chassis. Thus all these relationships depict “HAS-A” relationships that represent a whole object contained in another class. This is defined as Composition.

Relationships depicted by composition are dependent on each other. For Example, a Chassis cannot exist without a Vehicle. Similarly, Salary cannot exist without an Employee.

We can represent the composition diagrammatically as shown below:

Composition

The composition is also termed Containment. In the above representation, we have shown a parent class. In contrast to inheritance, we include a child class object inside the parent class. This is containment or composition.

Let us take a programming Example to understand this.

#include <iostream>
using namespace std;
//Composition example
//Child class - address
class Address {
   public:
   string houseNo, building, street, city, state;
   //Initialise the address object
   Address(string houseNo,string building,string street, string city, string state)
   {
      this->houseNo = houseNo;
      this->building = building;
      this->street = street;
      this->city = city;
      this->state = state;
   }
};
//Parent class - Employee
class Employee
{
   private:
   Address* address; //composition->Employee has an address
   public:
   int empId;
   string empName;
   Employee(int empId, string empName, Address* address)
   {
      this->empId = empId;
      this->empName = empName;
      this->address = address;
   }
   void display()
{
 
   cout<<empId <<" "<<empName<< " "<<endl;
   cout<<address->houseNo<< " "<<address->building<<" "<<address->street<<" "
   <<address->city<< " "<<address->state<<endl;
   }
};
int main()
{
   Address a1= Address("A-101","Silver Springs","Aundh","Pune","Maharashtra");
   Employee e1 = Employee(10001,"Ved",&a1);
   e1.display();
   return 0;
}

Output:

10001 Ved
A-101 Silver Springs Aundh Pune Maharashtra

In this example, we have a parent class Employee and a child class Address. Inside the parent class Employee, we have declared a pointer to the Address class and also initialize this object in the Employee constructor. Thus we depict the relationship that Employee has an Address which is composition.

How Should We Decide Between Composition And Inheritance?

Composition and inheritance both depict the relationships between classes. While inheritance depicts the “IS-A” relationship, the composition depicts “HAS-A” relationship.

Now the question is that when should we use inheritance and when should we use composition? Actually, we cannot decide on the exact situations as when we should use either of them. This is because each has its own advantages and disadvantages.

Both promote code reusability. Inheritance may make code bulky as the solutions get complex but at the same time, it also allows us to extend the existing code. Thus, we should use inheritance when our requirement is to modify and use the properties and method of another class inside the new class.

In other words, when we want to add more properties and extend the existing class. On the other hand, when we do not want to modify the properties and behavior of another class, but simply use it inside the class, we go for composition.

Thus the best decision is as to whether to use composition or inheritance will be made by weighing the pros and cons of both the techniques for the particular situation.

Conclusion

Thus, we have come to the end of our topic on inheritance. We have seen various modes of inheritance. We have also seen the types of inheritance, which we will explore in our next tutorial. We learned about the order of constructors that are executed in case of inheritance.

We also studied about templates and inheritance. We need to instantiate a template before we can use it in inheritance as the template itself is a data type and we cannot inherit from a data type.

The composition is another type of class relationship and we need to know the exact situation first and then only we can decide whether to use composition or inheritance.

In our upcoming tutorial, we will see more about the types of inheritance.

=> Watch Out The Simple C++ Training Series Here.