Inheritance

Inheritance:The C++ classes can be reused in several ways. Once a class has been written and tested, it can be adapted by other programmers to suit their requirements. This is basically done by creating new classes, reusing the properties of the existing ones. The mechanism of deriving a new class from an old one is called inheritance ( or derivation ). The old class is referred to as the base class (superclass) and the new one is called the derived class (subclass). Inheritance is not only used for re-using the classes, but as major aid for structuring the complex problems, breaking them into meaningful modular objects representing the problem to be solved. 

Different forms of Inheritance:
The derived class inherits some or all of the traits from the base class. A class can also inherit properties from more than one or from more than one level. A derived class with only one base class is called single inheritance and one with several base classes is called multiple inheritance. 
The traits of one class may be inherited by more than one class. This process is known as hierarchical inheritance. The mechanism of deriving a class from another derived class is known as multilevel inheritance. 
The following figure shows the different forms of inheritance. The direction of arrow indicates the direction of inheritance. 



The most important benefit inheritance brings is the ability to classify a complex problem or knowledge base into hierarchical classification. For example Alto is part of the classification of Maruti Cars or Compact Cars or both (Multiple inheritance), which is under larger classifications of cars, which in turn is under the more general classification of Automobiles and so on. Without this concept each object would have to be defined explicitly. With the help of inheritance we can simple add those qualities that make it unique from the generic common form. 

Defining Derived Classes:
A derived class can be defined by specifying its relationship with the base class in addition to its own details. The general form of defining a derived class is: 

class derived-class-name : visibility-mode base-class-name
{
        ...............//
        ...............// members of derived class
        ...............//
}The colon indicates that the derived-class-name is derived from the base-class-name. The visibility-mode or access control is optional and if present, may be private ,public or protected. The default visibility mode is private.
Examples: 
class ABC: private XYZ                    //private derivation
{
           .............
           .............
};
class ABC: public XYZ                     //public derivation
{
           .............
           .............
};
class ABC: XYZ                                //private derivation by default
{
          ..............
          ..............
};

Typically the derived object has all the information that a base class has. In addition all the specialized information for the derived object is added at the end of the base class.

Notes:
Derived class can access the public and protected members of the base class.
Private members of the base class cannot be accessed directly.

For eg:  if Manager is derived from Employee then the representation can be as follows:


For the above example if we have emp and mgr as obejects of Employee and Manager class respectively and we have the respective pointers pemp* and pmgr*, it is always possible to use pemp* to represent both the base and derived class. 
                    pemp = &emp; //Valid;                     pemp = &mgr; //Still Valid 
pemp->Name, pemp->EMp_ID and pemp->Date_of_joining are also valid for the derived Manager object. Use of pemp* to represent Manager object has no side effects. Other way round, representing an Employee (base class object) with Manager* (derived object pointer) is not valid as pmgr->Date_of_Promotion_to_Mgr and pmgr->list_of_reportees are not valid for Employee object. 

Types of Inheritance:
C++ distinguishes three types of inheritance: public, private and protected. As a default, classes are privately derived from each other. Consequently, we must explicitly tell the compiler to use public inheritance. 

The type of inheritance influences the access rights to elements of the various superclasses. Using public inheritance, everything which is declared private in a superclass remains private in the subclass. Similarly, everything which is public remains public. The following table specifies the type of access of the base class members in the derived class.


The leftmost column lists possible access rights for elements of classes. The second to fourth column show the resulting access right of the elements of a superclass when the subclass is derived using the access control specified mentioned in the top row.
Relationship 
“Is-a relationship” 
Public Inheritance should be used when you want to have the subset of properties of the base class to the new class. This kind of relationship is called the “Is-a relationship”. (Generalization – Specialization). 
class Employee 

         private: 
                     std::string Name; 
                     int EmpID; 
                     std::string Date_of_Joining; 
}; 
class Manager : public Employee 

         private: 
                     std::string Date_of_Promotion_to_Mgr; 
                         ….. 
         public: 
                     // public members 
}; 
Here, Manager is an Employee. Every Manager is an Employee of the company. Every Manager object will have the properties of Employee objects too. Every operation that can be applied to Employee should also make sense when applied to Manager Objects. This is called “Is-a Relationship”. 

“Has-a relationship”
Public Inheritance should not be used when the base class is a component of the object described by the derived class. 

class Wheel 

         public: 
                    int size() const; 
}; 
class Car : public Wheel 

        private: 
                 // Data Member and functions. 
        public: 
                // Public Member data and functions 
}; 
A Car is not a specialization of Wheel class. But, a car has a Wheel. This inappropriate use of public inheritance allows users to apply Wheel operations on Car. 

Car mycar;
int cap = mycar.size(); // This is not what we want. This will return the size of wheel, not the size of car. 

There are two ways to represent this “Has-a relationship”

We can have a composition. 
class Car 

          private: 
                      Wheel W; 
          Public: 
                      // etc., 
}; 

Or we can also use private inheritance to achieve this. 
class Car : private Wheel 

          private: 
                 // Private Members 
         public: 
                // Public Members 
}; 
Private inheritance is not much used than public inheritance, because composition is simpler and usually works as well. There are some advantages / disadvantages using private / protected inheritance than composition / containment. 
Advantages: 
The derived class can override private base class’s virtual functions 
The derived class can access to the protected members of the base class
Disadvantages: 
The private / protected inheritance can introduce unnecessary multiple inheritance if you want your class to be publicly derived from other class.
Composition is simpler than private / protected inheritance in terms of access and code maintainability.


Constructors and Destructors in Derived Classes:
The important thing to note is, when both the derived and base classes contain constructors, the base constructor is executed first and then the constructor in the derived class is executed. 

Similarly when the object is destroyed, derived class destructor will be called first and then the base class destructor.

In case of multiple inheritance, the base classes are constructed in the order in which they appear in the declaration of the derived class. Similarly, in a multilevel inheritance, the constructors will be executed in the order of inheritance.
Passing parameters to the base class constructor:
Initialization list is the method of initializing the class objects in the constructor. The following is the syntax of initialization list. 
constructor (arglist) : initialization-section 

            assignment-section 

The assignment-section is nothing but the body of the constructor function and is used to assign initial values to its data member. 

The initialization section basically contains a list of initializations separated by commas. This section is used to provide initial values to the base constructors and also to initialize its own class members. Thus we can use either of the sections to initialize the data members of the constructor class.

The following program illustrates the use of initialization lists in the base and derived constructors. Also this demonstrates the execution order of inheritance.
// Initialization List in the constructors 
// Constructors and destructors called. 
#include <iostream> 
using namespace std; 
class Alpha 

        int x; 
        public: 
                // constructors 
                Alpha(int i) 
                { 
                        x = i; 
                        cout << "\n Alpha constructed"; 
                } 
               ~Alpha() 
                { 
                       cout << "\n Alpha destroyed"; 
                } 
               void show_alpha() 
               { 
                      cout << " X = " << x << "\n"; 
               } 
}; 
class Beta : public Alpha 

       float p, q; 
       public: 
                  // Constructors 
                Beta(int i, float a, float b):Alpha(i), p(a), q(b+p) 
                { 
                       cout << "\n Beta Constructed"; 
                } 
                ~Beta() 
                 { 
                       cout << "\n Beta destroyed"; 
                 } 
                void show_beta() 
                { 
                       cout << " P = " << p << "\n"; 
                       cout << " Q = " << q << "\n"; 
                } 
}; 
class delta 

       int d; 
       public: 
                 delta(int a) { d = a; cout << "\n Delta Constructed";} 
                 ~delta() { cout << "\n Delta destroyed"; } 
                 void show_delta() 
                 { 
                         cout << " d = " << d << "\n"; 
                 } 
}; 
class Gamma : public Beta, public delta 

        int u, v; 
        public: 
                  // Constructors 
                  Gamma(int a, int b, float c): Beta(a,c,c), delta(a), u(a) 
                   { 
                            v = b; 
                           cout << "\n Gamma constructed"; 
                   } 
                   ~Gamma() 
                   { 
                            cout << "\n Gamma destroyed"; 
                   } 
                  void show_gamma() 
                  { 
                           cout << " U = " << u << "\n"; 
                           cout << " V = " << v << "\n"; 
                  } 
}; 
int main() 

        Gamma g(2, 4, 2.5); 
        cout << "\n\n Display member values " << "\n\n"; 
        g.show_alpha(); 
        g.show_beta(); 
        g.show_delta(); 
        g.show_gamma(); 
   return 0; 

Above program produces following output: 
Alpha constructed 
Beta Constructed 
Delta Constructed 
Gamma constructed 

Display member values
X = 2 
P = 2.5 
Q = 5 
d = 2 
U = 2 
V = 4 
Gamma destroyed 
Delta destroyed 
Beta destroyed 
Alpha destroyed 


Replicated Classes and Virtual Base Class:

A modified version of the sample code that we had seen in the last week to understand the arguments passing to the base class constructor and order of execution of the constructor and destructor of the base & derived class is listed below:
#include <iostream> 
using namespace std; 
class Alpha 

           int x; 
           public:
                    // constructors 
                    Alpha(int i) 
                    { 
                           x = i; 
                           cout << "\n Alpha constructed"; 
                    } 
                   ~Alpha() 
                    { 
                           cout << "\n Alpha destroyed"; 
                    } 
                   void show_alpha() 
                   { 
                          cout << " X = " << x << "\n"; 
                   } 
}; 
class Beta : public Alpha 
{                                  
          float p, q;           public: 
                   // Constructors 
                   Beta(int i, float a, float b):Alpha(i), p(a), q(b+p) 
                   { 
                                 cout << "\n Beta Constructed"; 
                   } 
                   ~Beta() 
                   { 
                                 cout << "\n Beta destroyed"; 
                   } 
                  void show_beta() 
                  { 
                                  cout << " P = " << p << "\n"; 
                                  cout << " Q = " << q << "\n"; 
                  } 
}; 
class delta : public Alpha 

          int d; 
          public: 
                     delta(int i, int a): Alpha (i) { d = a; cout << "\n Delta Constructed";} 
                      ~delta() { cout << "\n Delta destroyed"; } 
                      void show_delta() 
                      { 
                                  cout << " d = " << d << "\n"; 
                      } 
}; 
//Gama is derived using Beta and delta. Both in turn are derived from Alpha. 
//As a result there are two Alpha objects are created for each Gama object. 
class Gamma : public Beta, public delta 

          int u, v; 
          public: 
                    // Constructors
                   Gamma(int a, int b, float c): 
                   Beta(a,c,c), delta(a,b), u(a) 
                   { 
                            v = b; 
                            cout << "\n Gamma constructed"; 
                   } 
                  ~Gamma() 
                   { 
                            cout << "\n Gamma destroyed"; 
                   } 
                  void show_gamma() 
                  { 
                           cout << " U = " << u << "\n"; 
                           cout << " V = " << v << "\n"; 
                  } 
}; 
int main() 


         Gamma g(2, 4, 2.5);
         cout << "\n\n Display member values " << "\n\n"; 
         g.show_alpha(); //Ambiguous For which Alpha object? 
         g.show_beta(); 
         g.show_delta(); 
         g.show_gamma(); 
       return 0; 


The above example shows that ambiguity may be introduced when multiple base classes are inherited that in turn inherit from a common base class.

One way to remove this ambiguity is to manually resolve it by using the scope resolution operator. You may change the ambiguous statement as:

g.Beta::show_alpha() 

// Now it is clear that it has use the copy of Alpha that is
// created as part of Beta. 

This is not the perfect solution as in most of the cases intention is not to create multiple copies of the base class any way. C++ provides the concept of virtual base class to address this issue. If you declare a base class as virtual while inheriting only one copy of the base class object is created even if it is derived through multiple classes. The above example is modified using the virtual base class to have only single copy of Alpha in Gama:
#include <iostream> 
using namespace std; 
class Alpha 

        int x; 
        public: 
                 // constructors 
                Alpha(int i=0) 
                { 
                          x = i; 
                          cout << "\n Alpha constructed"; 
                } 
                ~Alpha()
                { 
                          cout << "\n Alpha destroyed"; 
                } 
                void show_alpha() 
                { 
                          cout << " X = " << x << "\n"; 
                } 
}; 
class Beta : virtual public Alpha 

           float p, q; 
           public: 
                      // Constructors 
                      Beta(int i, float a, float b): p(a), q(b+p) 
                      { 
                               cout << "\n Beta Constructed"; 
                      } 
                      ~Beta() 
                      { 
                               cout << "\n Beta destroyed"; 
                      } 
                      void show_beta() 
                       { 
                               cout << " P = " << p << "\n"; 
                               cout << " Q = " << q << "\n"; 
                        } 
}; 
class delta : virtual public Alpha 

          int d; 
          public: 
                     delta(int i, int a) { d = a; cout << "\n Delta Constructed";} 
                     ~delta() { cout << "\n Delta destroyed"; } 
                      void show_delta() 
                      { 
                               cout << " d = " << d << "\n"; 
                      } 
}; 
// Gama is derived using Beta and delta. Both in turn are derived from Alpha. 
// Since both have derived Alpha as virtual base, there will be only one 
// copy of Alpha in Gama. 

class Gamma : virtual public Beta, virtual public delta

         int u, v; 
         public: 
                   // Constructors, Explicitly calling the constructed of an 
                   // indirect virtual base class. 
                  Gamma(int a, int b, float c): Alpha(a), delta(a,b), Beta(a,c,c), u(a) 
                  { 
                           v = b; 
                          cout << "\n Gamma constructed"; 
                  } 
                  ~Gamma() 
                  { 
                          cout << "\n Gamma destroyed"; 
                  } 
                 void show_gamma() 
                 { 
                          cout << " U = " << u << "\n"; 
                          cout << " V = " << v << "\n"; 
                 } 
}; 
int main() 

         Gamma g(2, 4, 2.5); 
         cout << "\n\n Display member values " << "\n\n"; 
         g.show_alpha(); 
         g.show_beta(); 
         g.show_delta(); 
         g.show_gamma(); 
    return 0; 

Following is the output produced by the above program: 
Alpha constructed 
Beta Constructed 
Delta Constructed 
Gamma constructed 
Display member values 
X = 2 
P = 2.5 
Q = 5 
d = 4 
U = 2 
V = 4 
Gamma destroyed 
Delta destroyed 
Beta destroyed 

Please note that even though both Beta and delta have declared Alpha as virtual, a copy of Alpha is still present in objects their type.
Please also note that Alpha’s constructor is explicitly called from Gama to initialize the value of x. If it not called explicitly then the default constructor will be called and the value of the x would be 0. 

Important points:
  • Arguments for the base class’ constructor have to be defined in the definition of a derived class’ constructor. 
  • A derived class constructor can only specify initializer for it’s own members and immediate base class only. It can’t initialize members of a base. 
  • For example in the above case Gama constructor can’t pass the argument for Alpha constructor directly. It has to do it through Beta. Secondly it can’t initialize members of base directly like 
                       Gama (int a, int b, float c) : Beta (a,c,c), d(a), u(a) 

                       //Invalid, d is member of delta base class.
  • Private inheritance (has-a relationship) can be converted into containment. Whenever possible use containment and if required use private inheritance. 
  • Use Multiple Inheritance judiciously. 
  • Multiple inheritances produce duplicate objects. This can be resolved using virtual public inheritance. 

No comments:

Post a Comment