The best programs are written so that computing machines can perform them quickly and so that human beings can understand them clearly. A programmer is ideally an essayist who works with traditional aesthetic and literary forms as
well as mathematical concepts, to communicate the way that an algorithm works and to convince a reader that the results will be correct. Donald E. Knuth

Polymorphism

What is Polymorphism ?
Polymorphism is the ability of a message to be processed in more than one way. A different member function can be invoked at different instances depending on the object which is invoking the function. Function Overloading provides the concept of static or compile time polymorphism. During compilation phase, it is known which function will be called by an object when the program executes. Operator Overloading is a classic use case of this type of polymorphism. This is also known as early binding because object is bound to its function call during compilation phase.

Run Time Polymorphism
This type of polymorphism is also known as dynamic or late binding because a member function to be invoked by an object is decided at runtime. Suppose a base class and a derived class define functions which are exactly same ( name and argument list ). In this case function overloading is not valid. Now, if we declare a pointer to the base class, that pointer may point to an object of derived class but we won't be able to access the function in derived class. To address this issue, the concept of virtual function is used. Consider the program below :

#include<iostream>
#include<string>
using namespace std;

class fruit {
public:
   fruit() { }
   void display() {
      cout << "\nDisplay Fruit " << endl;
   }
   virtual void info() { // virtual function
      cout << "I am a fruit" << endl;
   }
};

class mango : public fruit {
public :
   mango() { }
   void display() {
      cout << "\nDisplay mango" << endl;
   }
   void info() {
      cout << "My name is Mango" << endl;
   }
   void show() {
      cout << "Showing Mango" << endl;
   }
};

int main() {
   fruit *ptr; // pointer to the base class
   fruit f; // declare an object of base class
   mango m; // declare an object of derived class

   /* select the object at run time and call the appropriate member function */
   ptr = &f; // points to 'fruit' object
   ptr->display(); // calls display() of class 'fruit';
   ptr->info(); // calls info() of class 'fruit' 
   ptr = &m; // points to 'mango' object
   ptr->display(); // calls display() of class 'fruit' ( we don't want this )
   ptr->info(); // calls info() of class 'mango'(since info() is virtual)
   //ptr->show(); // compilation error 
   return 0;
}

In the above program, we see that display( ) function is bound to fruit object at compile time but the binding of info() function is deferred till run time since it is virtual. So, we could able to access info() function of derived class using pointer to base class and run time polymorphism is achieved. Remember, Function Pointers are another way to achieve dynamic binding but it is a concept which originated in C. In C++ object oriented design, we use Virtual Functions.
The use case of accessing derived class members using pointer to a base class can be explained using a classic scenario. Consider a base class shape from which two classes rectangle and triangle are derived. We can see a is-a relationship here ( rectangle is-a shape and triangle is-a shape ). Base class define functions setParams( ) and getArea( ) which are overridden by derived classes to suit their requirements. At run time, any shape ( rectangle or triangle ) can be selected and thus appropriate member functions should be accessed. This also introduces the notion of abstraction on design level. Class shape abstracts rectangle and triangle. We just need to know the interfaces ( functions ) exposed by class shape to perform any operation on any of the derived classes. Following program illustrates the concept :

#include<iostream>
using namespace std;

/* class 'shape' provides common interface for 'rectangle'
   and 'triangle' class */
class shape {
protected :
   int width, height;
public :
   shape() { }
   // this function is inherited by the derived classes
   void setParams(int a, int b) {
      width = a;
      height = b;
   }
   // this function is re-defined in the derived classes
   virtual void getArea() {
      cout << "No shape selected" << endl;
   }
};

class rectangle : public shape {
public :
   rectangle() { }
   void getArea() {
      float area = width * height;
      cout << "\nLength : " << height << "  Breadth : " << width;
      cout << "\nArea of Rectangle : " << area << endl;
   }
};

class triangle : public shape {
public :
   triangle() { }
   void getArea() {
      float area = 0.5 * width * height;
      cout << "\nBase : " << width << "  Height : " << height;
      cout << "\nArea of Triangle : " << area << endl;
   }
};

int main() {
   shape *ptr; // pointer to the 'shape' class
   rectangle rect;
   triangle tr;

   /* select any object at run time */
   ptr = &rect; // 'rectangle' object selected
   ptr->setParams(5,7);
   ptr->getArea();

   ptr = &tr; // 'triangle' object selected
   ptr->setParams(9,3);
   ptr->getArea();
   return 0;
}


Pure Virtual Function
In the above program, we could not define the getArea( ) function in the shape class meaningfully. In such kind of situation, we can declare the function getArea( ) as pure virtual function. The presence of a pure virtual function in a class makes the class abstract i.e no object can be declared for that class. Also, the derived class must define the pure virtual function of the base class or it should be redeclared as pure virtual in the derived class. Following class definition shows how pure virtual function is used :

class shape {
protected :
   int width, height;
public :
   shape() { }
   void setParams(int a, int b) {
      width = a;
      height = b;
   }
   virtual void getArea() = 0; // pure virtual function
};

Back | Next