Definition

Polymorphism is one of the essential features of object-oriented programming that allows the class object to behave differently at different times. It is divided into two types.

  • Compile time polymorphism
  • Runtime polymorphism

Compile Time Polymorphism

Compile time polymorphism is also known as early binding or static polymorphism. In this type of polymorphism, object’s method is invoked at the compile time. It is further divided into operator overloading and function overloading.

Operator Overloading is a type of polymorphism in which an operator is overloaded to give user defined meaning to it. Overloaded operator is used to perform operation on user-defined data type.

// Operator overloading example
class Complex { 
private: 
  int real, imag; 
public: 
  Complex(int r = 0, int i =0)  {
    real = r;
    imag = i;
  } 

  // Called when '+' is used with between two Complex objects 
  Complex operator + (Complex const &obj) { 
    Complex res; 
    res.real = real + obj.real; 
    res.imag = imag + obj.imag; 
    return res; 
  }

  void print() {
    cout << real << " + i" << imag << endl; 
  }
};

int main() 
{
  Complex c1(10, 5), c2(2, 4); 

  // Example call to "operator+" 
  Complex c3 = c1 + c2;
  c3.print(); 
}

Function Overloading is a type of polymorphism in which there are many functions with the same name but different number or type of arguments. There are two ways by which we can overload any function.

  • By changing the number of argument
  • By changing the type of the argument

Two functions having the same name and same parameter list but different return type is not an overloaded function and will result in a compilation error.

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

class Sum {

public:
  // Overloaded function
  int add(int num1, int num2) {
    return num1 + num2;
  }

  // Overloaded function
  int add(int num1, int num2, int num3) {
    return num1 + num2 + num3;
  }

  // Overloaded function
  string add(string s1, string s2) {
    return s1 + s2;
  }
};

int main(void) {

  Sum obj;
  cout << obj.add(2, 5) << endl;
  cout << obj.add(8, 10, 1) << endl;
  cout << obj.add("Hi,", "Hello");

  return 0;
}

Runtime Polymorphism

Runtime polymorphism is also known as dynamic or late binding or dynamic polymorphism. In the case of runtime polymorphism, the object’s method is invoked at run time.

This type of polymorphism is achieved by Function Overriding. Function overriding occurs when a derived class has a definition for one of the member functions of the base class. That base function is said to be overridden. Function from a base class that is overriden should have the same signature as that of derived class.

#include <iostream>
using namespace std;

class Base
{
public:

  void show1()
  {
    cout <<__FUNCTION__ << endl;
  }

  virtual void show2()
  {
    cout << __FUNCTION__ << endl;
  }
};

class Derived :public Base
{
public:

  // Function overridden from base
  void show1() 
  {
    cout << __FUNCTION__ << endl;
  }

  void show2()
  {
    cout << __FUNCTION__ << endl;
  }
};

int main()
{
  Base b;
  Base* bPtr; //Base class pointer 
  Derived d;
  
  // Derived class object 
  bPtr = &d; 

  // Early Binding
  bPtr->show1(); 

  // Late Binding
  bPtr->show2();
}

// Output
Base::show1
Derived::show2

Member function show1() has been declared in the base class and later redefined in derived class. Irrespective of what type object the base pointer is holding, the program outputs the contents of the function of the class whose base pointer is the type of.

Member function show2() has been declared as virtual in the base class and later redefined in derived class. virtual keyword allow a member of a derived class with the same name as one in the base class to be appropriately called from a pointer. Since base class pointer contains derived class object, the show2 is bound to function show2 of derived class and hence the output.