Skip to main content

Polymorphism

Introduction


Polymorphism

Polymorphism is the ability to associate more than one implementation with the same operation. The binding of the operation with the implementation, can be decided at compile time or at run time. In object orientation, polymorphism can exist in two ways.
  • Hierarchy independent - This takes the form of overloading, where the same name of an operation, has different implementations in different classes and these classes need not be related by class hierarchy.
  • Hierarchy dependent- Here, an operation defined in the superclass can have any number of implementations along the class hierarchy. This type of polymorphism is referred to as universal polymorphism or overriding. The operation can be bound to any of the implementations. This resolution can take place, at compile time or at run time.

Pointers


Address and Pointers

The ideas behind pointers are not complicated. Here’s the first key concept: Every byte in the computer’s memory has an address. Addresses are numbers, just as they are for houses on a street. The numbers start at 0 and go up from there—1, 2, 3, and so on. If you have 1MB of memory, the highest address is 1,048,575. (Of course you have much more.)
Your program, when it is loaded into memory, occupies a certain range of these addresses. That means that every variable and every function in your program starts at a particular address.

Pointers to void

Before we go on to see pointers at work, we should note one peculiarity of pointer data types.Ordinarily, the address that you put in a pointer must be the same type as the pointer. You can’t assign the address of a float variable to a pointer to int, for example:
float flovar = 98.6;
int* ptrint = &flovar; //ERROR: can’t assign float* to int*
However, there is an exception to this. There is a sort of general-purpose pointer that can point to any data type. This is called a pointer to void, and is defined like this:
void* ptr; //ptr can point to any data type
Such pointers have certain specialized uses, such as passing pointers to functions that operate independently of the data type pointed to.
The next example uses a pointer to void and also shows that, if you don’t use void, you must be careful to assign pointers an address of the same type as the pointer. Here’s the listing for PTRVOID:
// ptrvoid.cpp
// pointers to type void
#include 
using namespace std;
int main()
{
int intvar; //integer variable
float flovar; //float variable
int* ptrint; //define pointer to int
float* ptrflo; //define pointer to float
void* ptrvoid; //define pointer to void
ptrint = &intvar; //ok, int* to int*
// ptrint = &flovar; //error, float* to int*
// ptrflo = &intvar; //error, int* to float*
ptrflo = &flovar; //ok, float* to float*
ptrvoid = &intvar; //ok, int* to void*
ptrvoid = &flovar; //ok, float* to void*
return 0;
}
You can assign the address of intvar to ptrint because they are both type int*, but you can’t assign the address of flovar to ptrint because the first is type float* and the second is type int*. However, ptrvoid can be given any pointer value, such as int*, because it is a pointer to void.
If for some unusual reason you really need to assign one kind of pointer type to another, you can use the reinterpret_cast. For the lines commented out in PTRVOID, that would look like this:
ptrint = reinterpret_cast<int*>(flovar);
ptrflo = reinterpret_cast<float*>(intvar);
The use of reinterpret_cast in this way is not recommended, but occasionally it’s the only way out of a difficult situation. Static casts won’t work with pointers. Old-style C casts can be used, but are always a bad idea in C++.

Base class Pointers

In C++ a pointer of a base class can point to an object of any of its derived class.
In the example given below shows what happens when a base class and derived classes all have functions with the same name, and you access these functions using pointers. Here is the listing of BASEPOINT
// basepoint.cpp
// normal functions accessed from pointer
#include 
using namespace std;
////////////////////////////////////////////////////////////////
class Base //base class
{
public:
      void show(){ cout << "Base\n"; }
};
////////////////////////////////////////////////////////////////
class Derv1 : public Base //derived class 1
{
public:
      void show()
        { cout << "Derv1\n"; }
};
////////////////////////////////////////////////////////////////
class Derv2 : public Base //derived class 2
{
public:
      void show()
           { cout << "Derv2\n"; }
};
////////////////////////////////////////////////////////////////
int main()
{
Derv1 dv1; //object of derived class 1
Derv2 dv2; //object of derived class 2

Base* ptr; //pointer to base class

Derv1 *pdv1; //pointer to Derv1
Derv2 *pdv2; //pointer to Derv2


ptr = &dv1; //put address of dv1 in pointer
ptr->show(); //execute show()

ptr = &dv2; //put address of dv2 in pointer
ptr->show(); //execute show()

ptr = &dv1; //put address of dv1 in pointer
if(reinterpret_cast<Derv1 *>(ptr) !=0){
  pdv1 =  reinterpret_cast<Derv1 *>(ptr);
  pdv1->show();
}
ptr = &dv2; //put address of dv2 in pointer
if(reinterpret_cast<Derv2 *>(ptr) !=0){
  pdv2 =  reinterpret_cast<Derv2 *>(ptr);
  pdv2->show();
}
return 0;
}
The Derv1 and Derv2 classes are derived from class Base. Each of these three classes has a member function show(). In main() we create objects of class Derv1 and Derv2, and a pointer to class Base. Then we put the address of a derived class object in the base class pointer in the line
ptr = &dv1; // derived class address in base class pointer
But wait—how can we get away with this? Doesn’t the compiler complain that we’re assigning an address of one type (Derv1) to a pointer of another (Base)? On the contrary, the compiler is perfectly happy, because type checking has been relaxed in this situation, for reasons that will become apparent soon. The rule is that pointers to objects of a derived class are type-compatible with pointers to objects of the base class.
Now the question is, when you execute the line
ptr->show();
what function is called? Is it Base::show() or Derv1::show()? Again, in the last two lines of BASEPOINT we put the address of an object of class Derv2 in the pointer, and again execute
ptr->show();
Which of the show() functions is called here? The output from the program answers these questions:
Base
Base
As you can see, the function in the base class is always executed. The compiler ignores the contents of the pointer ptr and chooses the member function that matches the type of the pointer, as shown in Figure below.
pointeraccess.jpg
Sometimes this is what we want, but it doesn’t solve the problem posed at the beginning of this section: accessing objects of different classes using the same statement.
You can use reinterpret_cast to cast the pointer of it's type and use it. But that doesn't solve the problem if u have hundreds of derived class from the base class.

Virtual Functions

Virtual means existing in appearance but not in reality. When virtual functions are used, a program that appears to be calling a function of one class may in reality be calling a function of a different class. Why are virtual functions needed? Suppose you have a number of objects of different classes but you want to put them all in an array and perform a particular operation on them using the same function call. For example, suppose a geometry program includes several different shapes: a triangle, a circle, a square, and so on. Each of these classes has a member function getArea() that returns the area of the object.
Now suppose you plan to calculate the area by grouping a number of these elements together, and you want to calculate in a convenient way. One approach is to create an array that holds pointers to all the different objects. The array might be defined like this.
shape *ptrarr[100]; //array of 100 pointers to shapes
If you insert pointers to all the shapes into this array, you can then calculate area using a simple loop:
 for(int j=0; j<N; j++)
 cout << ptrarr[j]->getArea() << endl;
This is an amazing capability: Completely different functions are executed by the same function call. If the pointer in ptrarr points to a square, the square function that returns area is called; if it points to a triangle, the triangle area function is called. This is called polymorphism, which means different forms. The functions have the same functionality, the getArea(), but different actual functions are called, depending on the contents of ptrarr[j]. Polymorphism is one of the key features of object-oriented programming, after classes and inheritance.
For the polymorphic approach to work, several conditions must be met.
  • First, all the different classes of shapes, such as circle and triangles, must be descended from a single base class (called shape in Geometry).
  • Second, the getArea() function must be declared to be virtual in the base class.

Virtual Member Functions Accessed with Pointers

Let’s make a single change in our program (BASEPOINT): We’ll place the keyword virtual in front of the declarator for the show() function in the base class. Here’s the listing for the resulting program,VIRTPOINT:
// virtpoint.cpp
// virtual functions accessed from pointer
#include 
using namespace std;
////////////////////////////////////////////////////////////////
class Base //base class
{
public:
      virtual void show(){ cout << "Base\n"; }
};
////////////////////////////////////////////////////////////////
class Derv1 : public Base //derived class 1
{
public:
      void show()
        { cout << "Derv1\n"; }
};
////////////////////////////////////////////////////////////////
class Derv2 : public Base //derived class 2
{
public:
      void show()
           { cout << "Derv2\n"; }
};
////////////////////////////////////////////////////////////////
int main()
{
Derv1 dv1; //object of derived class 1
Derv2 dv2; //object of derived class 2
Derv1 *pdv1;
Derv2 *pdv2;

Base* ptr; //pointer to base class
ptr = &dv1; //put address of dv1 in pointer
ptr->show(); //execute show()

ptr = &dv2; //put address of dv2 in pointer
ptr->show(); //execute show()

ptr = &dv1; //put address of dv1 in pointer

if(dynamic_cast<Derv1 *>(ptr) !=0){
  pdv1 =  reinterpret_cast<Derv1 *>(ptr);
  pdv1->show();
}
ptr = &dv2; //put address of dv2 in pointer
if(dynamic_cast<Derv2 *>(ptr) !=0){
  pdv2 =  reinterpret_cast<Derv2 *>(ptr);
  pdv2->show();
}

system("PAUSE");
return 0;
}
The output of this program is
  • Derv1
  • Derv2
  • Derv1
  • Derv2
Now, as you can see, the member functions of the derived classes, not the base class, are executed. We change the contents of ptr from the address of Derv1 to that of Derv2, and the particular instance of show() that is executed also changes. So the same function call
ptr->show();
executes different functions, depending on the contents of ptr. The rule is that the compiler selects the function based on the contents of the pointer ptr, not on the type of the pointer, as in BASEPOINT. This is shown in Figure below.
vpointeraccess.jpg
You can use dynamic_cast operator to cast to a particular derived type for the polymorphic classes.

Late Binding

The astute reader may wonder how the compiler knows what function to compile. In BASEPOINT the compiler has no problem with the expression
ptr->show();
It always compiles a call to the show() function in the base class. But in VIRTPOINT the compiler doesn’t know what class the contents of ptr may contain. It could be the address of an object of the Derv1 class or of the Derv2 class. Which version of draw() does the compiler call? In fact the compiler doesn’t know what to do, so it arranges for the decision to be deferred until the program is running. At runtime, when it is known what class is pointed to by ptr, the appropriate version of draw will be called. This is called late binding or dynamic binding.(Choosing functions in the normal way, during compilation, is called early binding or static binding.) Late binding requires some overhead but provides increased power and flexibility.
We’ll put these ideas to use in a moment, but first let’s consider a refinement to the idea of virtual functions.

Abstract Classes and Pure Virtual Functions

Think of the shape class. We’ll never make an object of the shape class; we’ll only make specific shapes such as circles and triangles. When we will never want to instantiate objects of a base class, we call it an abstract class. Such a class exists only to act as a parent of derived classes that will be used to instantiate objects. It may also provide an interface for the class hierarchy.
How can we make it clear to someone using our family of classes that we don’t want anyone to instantiate objects of the base class? We could just say this in the documentation, and count on the users of the class to remember it, but of course it’s much better to write our classes so that such instantiation is impossible. How can we can do that? By placing at least one pure virtual function in the base class. A pure virtual function is one with the expression =0 added to the declaration. This is shown in the VIRTPURE example.
// virtpure.cpp
// pure virtual function
#include 
using namespace std;
////////////////////////////////////////////////////////////////
class Base //base class
{
public:
 virtual void show() = 0; //pure virtual function
};
////////////////////////////////////////////////////////////////
class Derv1 : public Base //derived class 1
{
public:
 void show()
  { cout << “Derv1\n”; }
};
////////////////////////////////////////////////////////////////
class Derv2 : public Base //derived class 2
{
public:
 void show()
 { cout << “Derv2\n”; }
};
////////////////////////////////////////////////////////////////
int main()
{
// Base bad; //can’t make object from abstract class
Base* arr[2]; //array of pointers to base class
Derv1 dv1; //object of derived class 1
Derv2 dv2; //object of derived class 2
arr[0] = &dv1; //put address of dv1 in array
arr[1] = &dv2; //put address of dv2 in array
arr[0]->show(); //execute show() in both objects
arr[1]->show();
return 0;
}
Here the virtual function show() is declared as
virtual void show() = 0; // pure virtual function
The equal sign here has nothing to do with assignment; the value 0 is not assigned to anything. The =0 syntax is simply how we tell the compiler that a virtual function will be pure. Now if in main() you attempt to create objects of class Base, the compiler will complain that you’re trying to instantiate an object of an abstract class. It will also tell you the name of the pure virtual function that makes it an abstract class. Notice that, although this is only a declaration, you never need to write a definition of the base class show(), although you can if you need to.
Once you’ve placed a pure virtual function in the base class, you must override it in all the derived classes from which you want to instantiate objects. If a class doesn’t override the pure virtual function, it becomes an abstract class itself, and you can’t instantiate objects from it (although you might from classes derived from it). For consistency, you may want to make all the virtual functions in the base class pure. As you can see, we’ve made another, unrelated, change in VIRTPURE: The addresses of the member functions are stored in an array of pointers and accessed using array elements. This works in just the same way as using a single pointer. The output of VIRTPURE is the same as
VIRT:
  • Derv1
  • Derv2

Virtual Destructors

Base class destructors should always be virtual. Suppose you use delete with a base class pointer to a derived class object to destroy the derived-class object. If the base-class destructor is not virtual then delete, like a normal member function, calls the destructor for the base class, not the destructor for the derived class.

Points to remember

  • The process of resolving a function call to an object is called binding
  • Associating an object to a function call at run-time is referred to as late binding.
  • A pointer of a base class can point to an object of any of its derived class.
  • The virtual keyword is used to declare a function as virtual.
  • Virtual functions that have no body are called pure virtual functions.
  • A class containing one or more pure virtual functions is called an abstract class.
  • Instances of an abstract class cannot be created but pointers to it can created.
  • Static polymorphism involves binding of a function on the basis of the number, type and sequence of its arguments.
  • Function overloading is an example of static polymorphism because functions can be bound to the calls at compile time.
  • A function exhibits dynamic polymorphism when it exists in more than one form, and calls to its various forms are resolved dynamically when the program executed.
  • Static binding is considered to be more efficient while dynamic binding is more flexible.

Steps to attack the problem sets

  • Step 1: Read the Introduction and try sample programs given in the introduction(You can copy paste the programs)
  • Step 2: Go through the given PPT on Polymorphism and try sample problems given in the PPT.(you can copy paste the programs from the PPT)
  • Step 3: Go through the additional resources give for further clarification.
  • Step 4: Attack the problem sets in the given order(Problem Set A and then Problem Set B).
  • Step 5: If you are not clear with the problem sets then contact your respective mentor for clarification.
Spend at least 2-3 hours from Step 1 to Step 3.
Don't get confused with the example given in PPT and in the introduction part of this Module for shapes. In PPT, Shapes are used with respect to point in a plane and in introduction part shapes is used with respect to Geometry(area, sides, circumference etc).

Resources

PPT:
References:


Problem Sets


Problem Set A

1. Create a set of classes of Shapes (triangle, quadrilateral, circle) with the following inheritance relationship.
shapes.jpg
Declare and define the classes as
  • Shapes-- Abstract base class.
    • Triangle -- Abstract derived class with base class as Shapes
      • IsoscelesTriangle -- derived class with base class as Triangle.(Attribute with two sides)
      • EquilateralTriangle -- derived class with base class as Triangle(Attribute with one side)
    • Quadrilateral --Abstract derived class with base class as Shapes.
      • Rectangle -- derived class with base class as Quadrilateral.(Attribute with two sides-- length and breadth)
      • Square -- derived class with base class as Quadrilateral.(Attribute with one side)
      • Rhombus -- derived class with base class as Quadrilateral. (Attribute with two sides-- length and breadth)
    • Circle -- Derived class with base class as Shapes. (Attribute with radius)
Use the following declarations for the abstract class and the test main function. Write the code where ever necessary.
#include 
using namespace std;
////////////////////////////////////////////////////////////////
class Shapes //Abstract base class
{
public:
      virtual double getArea()=0; //Calculates the Area and returns the area
      virtual double getPerimeter()=0; //Calculates the perimeter and return the perimeter.
      virtual void getData()=0; //Take input from the user and store in the state of the object.
      virtual ~Shapes(){};
};

class Triangle : public Shapes  //Derived Abstract class
{
//Write your code here
};

class Quadrilateral : public Shapes //Derived Abstract class
{
public:
//Write your code here
};

//Write your code here to declare and define other classes stated

int main(){

//Write your code here to declare an array pointer of Shapes[6].


//Write a Loop for the Menu to create an instance of the object 
// andget the input from the user for the selected Shape 
//and add the pointer to the Shapes array.

//Write a Loop to Print the Area and Perimeter of the array shapes.

   system("PAUSE");       
}
Display a Menu Interface to the user to Create an instance of the Object. The Menu should be like.
  • Create an Instance of
    • Triangle
      • Create IsoscelesTriangle
      • Create EquilateralTriangle
    • Quadrilateral
      • Create Rectangle
      • Create Square
      • Create Rhombus
    • Create Circle

Create should create an instance of the derived class selected and then call the getData() function of that class and store that instance in the array of Shapes(Base Class).
You can add constructors for the derived classes with default values.
Write a test main function to take the input from the user for all the shapes and store them in a array, then print the area and perimeter of the class.
Refer: http://www.math.com/tables/geometry/index.htm
All the measurements is in Centimeters

Comments

Popular posts from this blog

Mini Project - Railway Reservation System

Module 1 Overview Railway Reservation System-(RRS) Project is developed to help students learn and realize capability of C programming language, and appreciate procedural approach of developing a system. RRS helps users reserve/cancel berths, search for trains, check reservation/cancellation history, etc... On the other hand it helps the railway reservation officers to prepare reservation charts, organize train schedules, etc... RRS provides two types of log-ins one is the User log-in for railway customers and the other one is Admin log-in for railway reservation officers. Project is divided into 10 modules of four hours duration each. All the modules will give exactly what to develop in the 4 hour time frame. Since most of the modules are inter-related it is important to keep track of the previous code. After submission it is allowed to make changes in the previous code to suit / integrate well to the system. Before we start the project, we need to make the followin...

Inheritance

Week 1 - M5 : Inheritance Learning Objectives At the end of this module, you will be able to Understand the implementation of inheritance in C++ Override the base class members Access overridden base class members using the scope resolution operator Understand the concept of base class initialization Introduction Inheritance The philosophy behind inheritance is to portray things as they exist in the real world. As inheritance is found in real world, it is important feature of OO programming. Inheritance has many advantages, the most important of them being the resuability of code. Once a class is defined and debugged, it can be used to create new subclasses. The reuse of existing class saves time and effort. The Class from which another class is derived is called the base class . The class which inherits the properties of the base class is called the derived class . Each instance of the derived class includes all the members of the base class. Since...