Monday, September 14, 2015

Chapter 12 - Inheritance and Polymorphism

                          

LETS LEARN

What is Inheritance

Types of Inheritance
      1. Single Inheritance ( only one super class ) 
      2. Multiple Inheritance ( several super classes ) 
      3. Hierarchical Inheritance ( one super class, many subclasses ) 
      4. Multi-level Inheritance ( Derived from a derived class ) 
      5. Hybrid Inheritance ( Derived from multiple derived classes ) 

 Base Class Access Control
 Inheritance And Private/Public Members
 Inheritance And Protected Members
 Inheriting Multiple Base Classes

 What is Polymorphism

 Types of Polymorphism
       1.      Static Polymorphism
       2.      Dynamic Polymorphism
 
 Pointers to Classes
 Pointers to Base Class
 Virtual Members
 Abstract Class
 "this" Pointer



INHERITANCE

Inheritance is the process of extending the data and functions of an existing Class (base class) to a new Class (derived class). The derived class can also contain its own member-data and member-functions. The base class can be referred to as parent/super class. The  derived class is also called child class.

Inheritance is one of the cornerstones of OOP because it allows the creation of hierarchical classifications. Using inheritance, you can create a general class that defines traits common to a set of related items. This class may then be inherited by other, more specific classes, each adding only those things that are unique to the inheriting class. In keeping with standard C++ terminology, a class that is inherited is referred to as a base class or super class. While, a class that does the inheriting is called the derived class.

 Furthermore, a derived class can be used as a base class for another derived class. In this way, multiple inheritance is achieved. Support of inheritance in C++ is both rich and flexible.

TYPES OF INHERITANCE


1.    Single Inheritance ( only one super class )

When a Derived Class is inherited from one Base Class, it is called Single inheritance.



2. Multiple Inheritance ( several super classes )

When a Derived Class is inherited from multiple Base Classes, it is called Multiple inheritance.


3. Hierarchical Inheritance ( one super class, many subclasses )

When more than one Derived Class is inherited from a Base Class, it is called Hierarchical inheritance.



4. Multi-level Inheritance ( Derived from a derived class )

When a Derived Class is inherited from another Derived Class, it is called Multi-level inheritance.



5. Hybrid Inheritance ( Derived from multiple derived class )

When a Derived Class is inherited by more than one type of inheritance, it is called Hybrid Inheritance.





BASE CLASS ACCESS CONTROL

When a class inherits another class, members of the base class become members of the derived class. Class inheritance uses this general form:

 class  derived-class-name : access-scope  base-class-name

  {

          /*   body of class  */

};


INHERITANCE AND PRIVATE/PUBLIC MEMBERS

The access-scope of base-class members inside the derived class is determined by access specifiers: public, private, or protected. If no access specifier is present, then the access specifier is private by default. “If the derived class is a struct, then public is the default” in the absence of an explicit access specifier.

When the access specifier for a base class is public, all public members of the base class become public members of the derived class, and all protected members of the base class become protected members of the derived class. Private member elements remain private to the base and are not accessible by members of the derived class.

PROGRAM 67

Q. Write a program using inheritance to create a derived class that assigns member data (integer value) to itself and prints it using its own functions. Also assign integer member data to its base class and print it using the function in base class.


#include <iostream>

using namespace std;

class base

{

    int i, j ;

    public:

          void set(int a, int b)

                      { i=a; j=b; }

          void show()

                      {

                        cout << "Values in Base Class\n";

                        cout << "i = "<<i<<"\t j = "<<j<<"\n\n";

                      }

};

class derived : public base

{

    int k ;

    public:

          derived(int x)

                       { k=x; }

          void show_k()

                       {

                         cout << "Values in Derived Class\n";

                         cout << "k = "<<k;

                       }

};

main ( )

{

          ob.set(10, 20);   // assign values to base class

           derived ob(30);  //assign value to derived class

          ob.show();         // print members of base class

          ob.show_k();     // print member of derived class

}


OUTPUT







Note: When the base class is inherited by using the private access specifier, all public and protected members of the base class become private members of the derived class.

INHERITANCE AND PROTECTED MEMBERS

The protected keyword is included in C++ to provide greater flexibility in the inheritance mechanism. When the member of a Class is declared as Protected, that member is accessible by all members of the Class, but is not accessible by non-member elements of the program. However, a Protected member can be accessed within its derived (child) class. By using access specifier “protected”, you can create class members that are private to their class, but they can still be inherited and accessed by a derived class.

As explained in the preceding section, a private member of a base class is not accessible by other parts of your program, including any derived class. However, protected members behave differently. If the base class is inherited as public, then protected members of the base class become protected members of the derived class and are, therefore, accessible by the derived class. Here is an example.

PROGRAM 68

Q. Write a program using inheritance to create a derived class that assigns integer values into protected members of its base class and prints them using base class functions. Multiply these same integer values of the base class and print the result using functions of the derived class.


#include <iostream>

using namespace std;

class base

{

   protected:

   int i, j;   // private to base class, but accessible by derived class

   public:

   void set (int a, int b) { i=a; j=b; }

   void show()

   {

      cout << "Values in Base Class\n";

      cout << "i = "<<i<<"\t j = "<<j<<"\n\n";

   }

};

class derived : public base

{

        int k;

        public:

         // derived class can access i and j of base class

         void setk()

                     { k=i*j; }

         void show_k()

                     {

                       cout << "Values Multiplied in Derived Class\n";

                       cout << "k = i X j = "<<k;

                     }

} ;

main()

 {

        derived ob;

        ob.set(10, 20);  // OK… of base class & known to derived class

        ob.show();        // OK… of base class & known to derived class

        ob.setk() ;        // OK… of derived class

        ob. show_k() ;   // OK… of  derived class

 }

OUTPUT






In this example, because the base class is inherited by derived class as public and because i and j are declared as protected, the derived Class function setk() is able to access them. If i and j had been declared as private by base, then derived Class would not have access to them, and the program would not compile.

When a derived class is used as a base class for another derived class, any protected member of the initial base class that is inherited (as public) by the first derived class may also be inherited as protected again by a second derived class.

For example, in the below given program, second level derived Class named “derived2” does have access to i and j.

PROGRAM 69

Q. Write a program using inheritance to create a derived Class that assigns integer values into protected members of its base class and prints them using base class functions. Multiply these integer values of the base class and print the result using functions of the derived class. Create a multi-level inherited Class from the existing derived class. Assign new values to base class and print them using base class function. Next, multiply and print them using first-level-derived class function. Then, add and print them using member functions of the second-level-derived class.

#include <iostream>

using namespace std;

class base

{

      protected:

             int i , j ;

      public:

             void set (int a, int b)

                   { i=a; j=b; }

             void show()

                    {

                           cout << "Values in Base Class\n";

                           cout << "i = "<<i<<"\t j = "<<j<<"\n\n";

                      }

};

// i and j inherited from Class ‘base’ by using public access

class derived1 : public base

{

    int k;

    public:

            void setk()

                 { k = i*j; }  // accessing i and j are allowed here

             void show_k()

                  {

                       cout << "Values Multiplied in Derived Class\n";

                       cout << "k = i X j = "<<k<<"\n\n"; 

                     }

};

//  i and j inherited indirectly through Class ‘derived1’

class derived2 : public derived1

{

      int m;

      public:

      void setm() { m = i+j; }   // accessing i and j are allowed here

      void show_m()

       {

          cout << "Values Added in Multi-level Derived Class\n";

          cout << "m = i + j = "<<m;

        }

 };

main()

{

         derived1 ob1;

         derived2 ob2 ;

         ob1.set (2, 3);

         ob1.show( ) ;

         ob1.setk( ) ;

         ob1.show_k( ) ;

         ob2.set(3, 4);

         ob2 .show( ) ;

         ob2.setk( ) ;

         ob2.show_k ( ) ;

         ob2.setm( ) ;

         ob2.show_m( ) ;

}

OUTPUT

















NOTE: If, however, the class base were inherited as private by class derived1, then all members of base would become private members of derived1, which means that they would not be accessible by derived2. In such cases, the class derived1 will still have access to public and protected elements of base class. However, class derived1 cannot pass along this privilege to class derived2.



INHERITING MULTIPLE BASE CLASSES

It is possible for a derived class to inherit two or more base classes. In the following example program, derived class inherits both base1 and base2.

PROGRAM 70

Q. Write a program to create 2 classes with one integer member and a print value function within each of them. Create a child class that inherits both the above base classes. Assign values to the integer variable of both base classes and print their corresponding values using function in the derived class.

   // An example of multiple inheritance

   #include <iostream>

   using namespace std;

   class A

   {

      protected:

      int x;

      public:

            void showx()

            {

                 cout<<"x in Class A : ";

                 cout << x<< "\n";

            }

   };

   class B

   {

      protected:

      int y;

      public :

            void showy()

            {

                 cout<<"y in Class B : ";

                 cout << y<< "\n";

            }

   };

   //  inheriting multiple base classes

   class C: public A, public B

   {

      public:

            void set(int i, int j)

               { x=i; y=j ; }

   };

   main ( )

   {

      C ob;

      ob.set(10, 20); // provided by derived class C

      ob.showx() ;    // from class A

      ob.showy();     // from class B

 }

OUTPUT




As the example demonstrates, to inherit more than one base class, use a comma-separated list. Further, be sure to use an access-specifier for each base class that is inherited.

POLYMORPHISM
Polymorphism means ‘many forms’ and in C++ it is used in functions and operators. Polymorphism is the ability of a specific (single) function or an operator to perform different tasks under different circumstancesLet’s consider a real world example of polymorphism.

Suppose moveAhead() is a C++ function to move any object. Then, 

  • for a Car, the moveAhead() would mean to “Drive”,
  • for a Ship, the moveAhead() would mean to “Sail”, 
  • for an Aeroplane, moveAhead() would mean to “Fly”, 
  • for a Bicycle, the moveAhead() would mean to “Pedal”.

So basically, when different objects (car, ship, aeroplane, bicycle) call and execute the same moveAhead() function, it would actually perform different operational methods (drive, sail, fly, pedal) to perform the task of moving ahead.





Types of Polymorphism

There are two types of polymorphism:

 

(i)                Static polymorphism or Compile time polymorphism

(ii)               Dynamic polymorphism or Run time polymorphism

 

Static Polymorphism is exhibited by overloaded functions. Static polymorphism is the technique of overriding a function by providing additional definitions with different numbers or types of parameters. The compiler matches the parameter list to the appropriate function that is called in the program. (Remember how we had used function polymorphism in the chapter on Functions). Here you declare and define multiple functions with same name and different parameters. For example, consider the following function declarations: 


          void add(int , int);

            void add(float, float);


Dynamic Polymorphism is exhibited by using late binding and is achieved by choosing correct “function-call” based on the “type of object” pointed by the pointer. A function is said to exhibit dynamic polymorphism when it exists in more than one form, and “calls” to its various forms are decided and executed dynamically when the program is executed.

In Dynamic polymorphism, there is a parent class having a declared and (or) defined virtual function. This virtual function can be redefined (rewritten) in a child class. At run-time, using appropriate Objects, you can either use the function within the parent class or within the child class.

The term late binding refers to the decision on using the required function (parent or child) at run-time instead of at compile-time. This feature increases the flexibility of the program by allowing the appropriate method (function) to be invoked (called).

For better understanding of this section, you need to know inheritance and must have a proper understanding of how the following works:

  • pointer to class
  • pointer to base class
  • virtual members


Pointer to Class

 

We can create pointers that point to classes by using the class name as the type for the pointer. For example: CRectangle * prect;

Here, prect is a pointer to an Object of Class CRectangle

In order to refer directly to a member of an object pointed by a pointer, we can use the arrow operator (->) of indirection. Here is an example with some possible combinations.

PROGRAM 71

Q. Create a class CRectangle. Assign values and print the area of corresponding rectangles. Use pointers to classes (note the different ways we have used here as references to a Class). Declare the following class members:

Member Data: height, width

Member Functions: void set_values (int, int),   int area (void)


#include <iostream>

using namespace std;

class CRectangle

{

    int width, height;

    public:

    void set_values (int, int);

    int area (void)

   {

           return (width * height);

   }

};

void CRectangle::set_values (int a, int b)

{

    width = a;

    height = b;

}

main ()

{

   CRectangle A, *B, *C;  // *B, *C are pointers to Class

   CRectangle *D = new CRectangle[2];  // pointer to array of Class

   B= new CRectangle;

   C= &A;

   A.set_values (10,20);

   B->set_values (30,40);

   D->set_values (50,60);     // assigns values to  D[0]

   D[1].set_values (70,80);   // assigns values to  D[1]

   cout << "Area of Class A                   : " << A.area() << endl;

   cout << "Area of Class pointed by *B : " << B->area() << endl;

   cout << "Area of Class pointed by *C : " << C->area() << endl;

   cout << "Area of Class Array D[0]    : " << D[0].area() << endl;

   cout << "Area of Class Array D[1]    : " << D[1].area() << endl;

}

OUTPUT










TRY IT YOURSELF

In main function of the above program, delete these 3 lines:

               CRectangle A, *B, *C;

               B = new CRectangle;

             C = &A;

Then add these 2 lines:

CRectangle A, *C = &A;

CRectangle *B = new CRectangle;

Q: Will it work? Try to understand memory allocation process here.




Pointers to Base Class

A pointer that is set to the base (parent) class can be used to access the derived (child) class. One of the key features of derived classes is that a pointer to the derived class is type-compatible with a pointer to its base-class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature that brings Object Oriented Methodologies to its full potential. Here’s an example program.


PROGRAM 72

Q. Create a base class CPolygon with data members (height, width) and member function (set_values()) to set these values. Derive 2 classes (CRectangle, CTriangle) with CPolygon as base class and member function (area()) to find area of "rectangle" or "triangle". Use pointer to base class concept. Assign values and print corresponding area via main().

 

#include <iostream>

using namespace std;


class CPolygon

{          protected:

              int width, height;

           public:

             void set_values (int a, int b)

                     { width=a; height=b; }

  };


class CRectangle: public CPolygon

{

           public:

             int area ()

                 { return (width * height); }

  };


class CTriangle: public CPolygon

{

           public:

             int area ()

               { return (width * height / 2); }

};


main ()

{

  CRectangle rect;

  CTriangle trng;

  CPolygon *ppoly1 = &rect;

  CPolygon *ppoly2 = &trng;

  ppoly1 -> set_values (4,5);

  ppoly2 -> set_values (4,5);

  cout<<"Area of Rectangle: "<<rect.area()<<" sq.units"<<endl;

  cout<<"Area of Triangle : "<<trng.area()<<" sq.units"<<endl;

}

OUTPUT






In the main function, we create two pointers that point to objects of base class CPolygon (ppoly1, ppoly2). Then we assign these two pointers with references to objects of derived classes (rect, trng). Since both these objects belong to classes that are derived from CPolygon, you can assign their address to the pointer of parent class. 

The only limitation in using *ppoly1 and *ppoly2 instead of rect and trng is that both *ppoly1 and *ppoly2 are of type CPolygon and therefore we can only use these pointers to refer to the members that CRectangle and CTriangle inherit from CPolygon. For that reason when we call the area() we have to use the objects rect and trng instead of the pointers *ppoly1 and *ppoly2.

In order to use area() with the pointers to class CPolygon, this member should also have been declared in the class CPolygon. But area() for Rectangle is different from area() for Triangle and therefore we cannot implement it in the base class. This is when virtual members become important. You can declare area() as virtual in the base class and then refer to area() using the pointer to base class (in previous program, the base class pointer points to derived class address).

Virtual Members

A member of any Class, which can be redefined in its Derived Classes, is known as a virtual member. In order to declare a member of a class as virtual, we must precede its declaration with the keyword virtual. A virtual member provides runtime polymorphism.


PROGRAM 73

Q. Create a base class CPolygon with data members (height, width) and member function (set_values()) to set these values. Derive 2 classes (CRectangle, CTriangle) with CPolygon as base class and member function (area()) to find area of rectangle or triangle. Use pointer to base class concept and area() as virtual member. Assign values and print corresponding area via main().

#include <iostream>

using namespace std;

class CPolygon

{

  protected:

               int width, height;

  public:

                void set_values (int a, int b)

                           { width=a; height=b; }

                virtual int area ()

                           { return (0); }

 };

class CRectangle: public CPolygon

{

  public:

                int area ()

                            { return (width * height); }

  };

class CTriangle: public CPolygon

{

  public:

                int area ()

                              { return (width * height / 2); }

 };

main ()

{

    CPolygon poly; CRectangle rect; CTriangle trgl;

    CPolygon *ppoly1 = &rect;

    CPolygon *ppoly2 = &trgl;

    CPolygon *ppoly3 = &poly;

    ppoly1 -> set_values (4,5);

    ppoly2 -> set_values (4,5);

    ppoly3 -> set_values (4,5);

    cout << ppoly1 -> area() << endl;

    cout << ppoly2 -> area() << endl;

    cout << ppoly3 -> area() << endl;

}


OUTPUT

20

10

 0








Here, all the three classes (CPolygon, CRectangle and CTriangle) have the same members: width, height, set_values() and area().

The member function area() has been declared as virtual in the base class because it is later redefined in each derived class. You can verify by removing this virtual keyword from the declaration of area() within CPolygon, and then when you run the program the result will be 0,0,0 for the three polygons instead of 20, 10 and 0. This is because, CPolygon::area() will execute for all the function calls, CRectangle::area(), CTriangle::area() or CPolygon::area(). Remember that, area() is executed via a pointer to Class-type CPolygon.

A class that declares or inherits a virtual function is called a polymorphic class.

Also note that despite area() being virtual, we have also declared an object of type CPolygon and called its own area(), which always returns 0.

ABSTRACT CLASS

Abstract Class is a special base class which contains at least one pure virtual function.

The pure virtual function is a function that is only declared in the base class, and is defined in the derived classes.

We can declare a pure virtual function by using a pure specifier (= 0) in the declaration of a virtual member function in the Class declaration.

Abstract base classes are very similar to the class CPolygon of the previous example. The only difference is that in our previous example we defined a valid area() within the base Class, with a minimal functionality for objects that were of class CPolygon (like the object poly), whereas in an abstract base class we would leave area() without any definition (implementation details). This is done by appending =0 (equal to zero) to the function declaration. An abstract base CPolygon class could look like this:


class CPolygon
{
       protected:
             int width, height;
       public:
            void set_values (int a, int b)
                 { width=a; height=b; }
            virtual int area () =0;
};

Notice how we appended =0 to virtual int area() instead of specifying an implementation for the function. This type of function is called a pure virtual function, and all classes that contain at least one pure virtual function are abstract base classes.

Difference between abstract base class and regular polymorphic class

The main difference between an abstract base class and a regular polymorphic class is that, in abstract base classes at least one of its members lacks implementation and we cannot create instances (objects) of it. But a class that cannot instantiate objects is not totally useless. We can create pointers to it and take advantage of all its polymorphic abilities. Therefore the declaration,

CPolygon poly;

would not be valid for an abstract base class, because it tries to instantiate an object. Nevertheless, the following pointers would be perfectly valid:

 CPolygon * ppoly1;

 

Hence for an abstract class, pointers to the abstract base class are used to point to objects of its derived classes and thereon, to implement its polymorphic functions.

PROGRAM 74

Q. Create a base class CPolygon with data members:
        height
        width
        set_values()

Derive 2 classes CRectangle, CTriangle from CPolygon as base class and use area() to find area of a rectangle or a triangle.

Use ABSTRACT class to declare and call area(). Assign the values and print corresponding area.

#include <iostream>

using namespace std;

class CPolygon

{

     protected:

               int width, height;

public:

              void set_values (int a, int b)

                         { width=a; height=b; }

              virtual int area (void) =0;

};

class CRectangle: public CPolygon

{

           public:

                int area (void)

                      { return (width * height); }

};

class CTriangle: public CPolygon

{

           public:

                int area (void)

                      { return (width * height / 2); }

};

main ()

 {

  CRectangle rect;

  CTriangle trng;

  CPolygon * ppoly1 = &rect;

  CPolygon * ppoly2 = &trng;

  ppoly1->set_values (4,5);

  ppoly2->set_values (4,5);

  cout << ppoly1->area() << endl;

  cout << ppoly2->area() << endl;

}

 

OUTPUT


20
   10

If you review the program you will notice that we refer to objects of different but related classes using a unique type of pointer to CPolygon. This can be tremendously useful. For example, now we can create a function member of the abstract base class CPolygon that is able to print on screen the result of the area() even though CPolygon itself has no implementation for this function. Virtual members and abstract classes grant C++ the polymorphic characteristics that make object-oriented programming such a useful instrument in big projects. Of course, we have seen very simple uses of these features, but these features can be applied to arrays of objects or dynamically allocated objects.

The "this" Pointer

“this” is a pointer that points to the object within which the member function is called or invoked. "this" pointer is accessible only within the non-static member functions of a class, struct, or union type.

Static member functions do not have a this pointer.

When a member function is called, it is automatically passed an implicit argument (this pointer) that is a pointer to the invoking object (the pointer points to function within the object). 

PROGRAM 75

 

Q. Write a program to create a class called pwr that computes the result of a number raised to some power. Also print memory size of the declared Object using “this” pointer.

 

#include <iostream>

using namespace std;

class pwr

{

   double b;

   int e ;

   double val ;

    public :

          pwr(double base, int exp);

          double get_pwr()

          {

             cout<<"\nSize of this Object is : "<<sizeof(this)<<" Bytes\n";

             return val;

          }

};

pwr :: pwr(double base, int exp)

{

   b = base;

   e = exp ;

   val = 1;

   if (exp==0) return;

   for(; exp>0; exp--)

          val = val * b;

}

main()

{

    pwr x(2, 3), y(2.5, 1), z(2.5, 0);

    cout<< "2 raised to 3   = " << x.get_pwr()<<"\n";

    cout<< "2.5 raised to 1 = " << y.get_pwr()<<"\n";

    cout<< "2.5 raised to 0 = " << z.get_pwr()<<" \n";

   }

 

 OUTPUT












Within a member function, the members of a class can be accessed directly, without any object or class qualification. Thus, inside pwr(), the statement

b = base;

means that the copy of b associated with the invoking object will be assigned the value contained in base. However, the same statement can also be written like this:

this -> b = base;

The this pointer points to the object that invoked pwr(). Thus, this -> b refers to that object’s copy of b. Below is the entire pwr() constructor re-written using the this pointer:

pwr :: pwr (double base,   int exp)

{

 this -> b = base;

 this -> e = exp;

 this -> val = 1;

 if (exp==0) return;

   for( ; exp>0; exp--)

 (this -> val) = (this -> val) * (this -> b);

 }

Actually, no C++ programmer would write pwr() as just shown above, because nothing is gained, and the standard form is easier. However, the this pointer is very important, when operators are overloaded and whenever a member function must utilize a pointer to the object that invoked it. Remember that this pointer is automatically passed onto all member functions. Therefore, get_pwr() can also be rewritten as shown here:

 double get_pwr()

 { return this -> val; }

 

If get_pwr() is invoked like this:

   y.get_pwr() ;

Then this pointer will point to object y.



Note : Two important points about this pointer


1. friend functions are not members of any Class, and so they have no this pointer.

2. static member functions do not have a this pointer.




No comments:

Post a Comment