C++ – Inheritance

The idea of inheritance implements an IS-A relationship between classes.

A class that inherits is called the derived class and the class from which the derived class inherits is called the base-class.

Types of Inheritance supported by C++

  • Single Level Inheritance – A class inherits from a single base class.
    class Animal{
        ......
    };
    
    class Mammal:public Animal{
        ......
    };
    
  • Multi-Level Inheritance – A class inherits from another derived class. In the example below, Dog inherits from Mammal which in turn inherits from Animal.
    class Animal{
        ......
    };
    
    class Mammal:public Animal{
        ......
    };
    
    class Dog:public Mammal{
        ......
    };
    
  • Hierarchical Inheritance – More than one class inherit from a base class. In the example below, both Mammal and Reptile inherit from Animal.
    class Animal{
        ......
    };
    
    class Mammal:public Animal{
        ......
    };
    
    class Reptile:public Animal{
        ......
    };
    
  • Multiple Inheritance – A class inherits from multiple base classes. In the example below, Bat inherits from both Bird and Mammal. (Technically, Bat is considered as a Mammal although it can fly.  But for the sake of this example, Bat is both a Mammal and a Bird 😀 .)
    class Bird{
        ......
    };
    
    class Mammal{
        ......
    };
    
    class Bat:public Animal, public Mammal{
        ......
    };
    
  • Hybrid Inheritance – A combination of more than one of the above types of inheritance is called as hybrid inheritance. Below example is a combination of multilevel, multiple and hierarchical types.
     class Animal{
         ......
     };
    
     class Mammal:public Animal{
         ......
     };
    
     class Bird:public Animal{
         ......
     };
     class Bat:public Mammal,public Bird{
         ......
     };
     

Inheritance may seem like an easy concept to grasp at first. But there are a few subtleties that are often overlooked. When a derived class inherits from a base class, the members of the base class can be looked at from two different perspectives.

The first perspective is that of the derived class and what it sees from within. A derived class can be thought of as having a base part and a derived part. The base part houses all of the base class members and the derived part houses all of the original members of the derived class. Only the protected members and the public members of the base part are directly accessible to the members within the derived part. Please note that it is still possible to indirectly access the private members of the base part (via the protected member functions and the public member functions of the base part).

The second perspective is that of someone looking at the derived/inheriting class from outside (the external interface of the derived/inheriting class). This is the perspective that most people already know. From this perspective, a derived class only inherits the protected members and the public members of the base class. Private members, constructors, overloaded operators, friend functions, friend classes and the destructor of the base class are never inherited. Inheritance can be public, protected or private. If not specified explicitly, compiler assumes private inheritance. In public inheritance, all protected members of the base class become the protected members of the derived class. All public members of the base class become the public members of the derived class. In protected inheritance, all protected members of the base class and all the public members of the base class become the protected members of the derived class. In private inheritance, all protected members of the base class and all the public members of the base class become the private members of the derived class.

#include <iostream>
class Base {
private:
int b;
protected:
int c;
public:
Base() :a(5), b(6), c(7) {}
int a;
};
class Derived1 : Base { //private Inheritance
public:
void printValues() {
std::cout << a; //ok
std::cout << b; //error. 'b' is a private member in Base and therefore inaccesible to Derived1.
std::cout << c; //ok
}
};
class Derived2 :protected Base { //protected Inheritance
public:
void printValues() {
std::cout << a; //ok
std::cout << b; //error. 'b' is a private member in Base and therefore inaccesible to Derived2.
std::cout << c; //ok
}
};
class Derived3 :public Base { //public Inheritance
public:
void printValues() {
std::cout << a; //ok
std::cout << b; //error. 'b' is a private member in Base and therefore inaccesible to Derived3.
std::cout << c; //ok
}
};
int main(int argc, char *argv[]) {
Derived1 d1;
std::cout << d1.a; //error. 'a' is a private member in Derived1
std::cout << d1.c; //error. 'c' is a private member in Derived1
Derived2 d2;
std::cout << d2.a; //error. 'a' is a protected member in Derived2
std::cout << d2.c; //error. 'c' is a protected member in Derived2
Derived3 d3;
std::cout << d3.a; //ok. 'a' is a public member in Derived3
std::cout << d3.c; //error. 'c' is a protected member in Derived3
return 0;
}
view raw Inheritance hosted with ❤ by GitHub

When a derived class is initialized, default constructor of the most-base class gets executed first followed by the default constructor of the class below it in the hierarchy. Constructor of the derived class is run at the end. This is so because the derived class might have to use the members of its parents and hence the parents need to be initialized before the initialization of the derived class. Only the default constructors of the parents are called implicitly. All other constructors will need to be called explicitly via the member initializer list. If none of the other base class constructors are explicitly called in the derived class and the default base class constructor cannot be found, compiler will throw an error.

But the destructor of a parent a called only after the destructor of its child. So,keep in mind that the constructors and destructors are called in the opposite order in a base class- derived class hierarchy. Please note that C++ prevents classes from initializing inherited member variables in the initialization list of its constructor.

Although C++ allows multiple inheritance, it needs to be used with caution.

  • If a class inherits from multiple classes and each of these classes define a function with the same name, it can cause a compiler error when you try to call this function from the derived class’s object. Always remember to qualify the function name with the class name using the scope resolution operator in these situations.
  • When you have a hybrid inheritance, there may be multiple paths from a Base class to the derived class. This will result in duplicate inherited members in the derived class. This is known as the Diamond problem. Use Virtual Inheritance to avoid this problem.

An important concept in OOPs terminology called run-time Polymorphism (method overriding) is also enabled by inheritance. But Method Overriding may lead to what is known as static binding. Make sure to declare the overridden base class method as virtual to let the compiler perform dynamic binding. Starting c++11, override keyword can be added to a derived class method if it overrides a virtual method in the base class.

A base class pointer pointing to a derived class object is a necessary evil. It is the use of such a pointer that leads to static binding that we just discussed. There are several other issues that may crop up via the use of such a pointer. Destructor of the base class must also be declared as virtual (Virtual Destructors) to avoid static binding and subsequent leak of memory held by the derived class object.

Starting c++11, If the base class must not be inherited, it can be declared as final. This will prevent it from being inherited by other classes.

Further Reading

Inheritance in Java
Java supports single level, multilevel and hierarchical inheritance but does not support multiple and hybrid inheritance. But interfaces can be utilized to mimic multiple inheritance. In Java jargon, a class which is inherited is called parent or superclass and the new class is called child or subclass. Other than the aforementioned behavior, inheritance in Java is pretty much similar to inheritance in C++.

Inheritance in Python
Python supports all of the above types of inheritance. In python jargon, a class that inherits is called the sub class and the class from which the sub class inherits is called the super class. In python, there are no issues related to static and dynamic binding and hence, method overriding is straightforward.

When a subclass is initialized, only its constructor (__init__()) is invoked. You will have to explicitly call the constructors of the parent classes.  Destructor(__del__()) also has a similar execution.

When there are conflicts due to multiple inheritance (like diamond problem), a function (when defined in multiple classes in the hierarchy) is searched in the superclasses in a left-right depth first manner. This is called as Method Resolution Order.

All members of a superclass are inherited by default by the subclasses. But Python loosely implements public, private and protected access modifiers using the underscore(‘_’) operator.

  • A member variable or function whose name starts with double underscore (‘__’) will not be available to the subclasses and cannot be accessed outside the class. These members are analogous to the private members in C++ and Java. Python performs something called as  name mangling to enforce this.
  • A member variable or function whose name starts with a single underscore (‘_’) is loosely analogous to the protected members in C++ and Java.
  • All other members are public.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: