- The term "Polymorphism" is the combination of "poly" + "morphs" which means many forms.
- It is a Greek word. In object-oriented programming, we use 3 main concepts: inheritance, encapsulation, and polymorphism.
- Compile time polymorphism: The overloaded functions are invoked by matching the type and number of arguments.
- This information is available at the compile time and, therefore, compiler selects the appropriate function at the compile time.
- It is achieved by function overloading and operator overloading which is also known as static binding or early binding.
- Now, let's consider the case where function name and prototype is same.
- Run time polymorphism: Run time polymorphism is achieved when the object's method is invoked at the run time instead of compile time.
- It is achieved by method overriding which is also known as dynamic binding or late binding.
- Inheritance is a mechanism in which one object naturally acquires all of its parent object's properties and behaviors.
- In C++, a pointer variable of a base class type can point to an object of its derived class.
- There are situations when this feature of C++ can be used to develop generic code for a variety of applications.
Pointer of base class
Consider the following program to understand pointer compatibility property
#include <iostream.h>
#include <conio.h>
class Shape
{
protected:
double width, height;
public:
void set_data (double a, double b)
{
width = a;
height = b;
}
};
class Rectangle: public Shape
{
public:
double area ()
{
return (width * height);
}
};
int main ()
{
Shape *sPtr; //declare pointer variables of type Shape
Rectangle Rect; //create the object rect of type Rectangle
sPtr = &Rect; //make sPtr point to the object rect.
sPtr->set_data (5,3); //set length and width of object rect
cout << sPtr -> area() << endl; //Compile Error !!
return 0;
}
- Notice that even though rectPtr is poining to rect (object of type Rectangle), when the program executes, the statement sets length and width of rectangle.
- If you tried to access area function of class Rectangle with sPtr it will give you compiler error.
sPtr -> area()
is a compiler error !
- It means base class pointer can not access the additional member function of its derived class.
- If we want to do this we need to type cast the base class pointer.
Using Type Casts with Base Class Pointers
- We can use a type cast to get the compiler to accept the statement:
static_cast <Rectangle *> (sPtr)->area()
so we should write the statement
- The type cast informs the compiler that sPtr is actually pointing to a Rectangle object derived from the Shape base class.
- In general, a pointer to a base class that actually points to a derived class object must first be appropriately cast before the additional features of the derived class can be used.
#include <iostream.h>
#include <conio.h>
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// Main function for the program
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
shape = &rec;
// call rectangle area.
shape->area();
// store the address of Triangle
shape = &tri;
// call triangle area.
shape->area();
return 0;
}
Expected Output:-
Parent class area
Parent class area
Parent class area
Parent class area
- The reason for the incorrect output is that the call of the function area() is being set once by the compiler as the version defined in the base class.
- This is called static resolution of the function call, or static linkage - the function call is fixed before the program is executed.
- This is also sometimes called early binding because the area() function is set during the compilation of the program.
- But now, let's make a slight modification in our program and precede the declaration of area() in the Shape class with the keyword virtual so that it looks like this:
class Shape
{
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
Expected Output:-
Rectangle class area
Triangle class area
Rectangle class area
Triangle class area
Run time Polymorphism Example: By using two derived class
#include <iostream.h>
#include <conio.h>
class Shape { // base class
public:
virtual void draw(){ // virtual function
cout<<"drawing..."<<endl;
}
};
class Rectangle: public Shape // inheriting Shape class.
{
public:
void draw()
{
cout<<"drawing rectangle..."<<endl;
}
};
class Circle: public Shape // inheriting Shape class.
{
public:
void draw()
{
cout<<"drawing circle..."<<endl;
}
};
int main(void) {
Shape *s; // base class pointer.
Shape sh; // base class object.
Rectangle rec;
Circle cir;
s=&sh;
s->draw();
s=&rec;
s->draw();
s=?
s->draw();
}
Expected Output:-
drawing...
drawing rectangle...
drawing circle...
Runtime Polymorphism with Data Members
- Runtime Polymorphism can be achieved by data members in C++.
- Let's see an example where we are accessing the field by reference variable which refers to the instance of derived class.
#include <iostream.h>
#include <conio.h>
class Animal { // base class declaration.
public:
string color = "Black";
};
class Dog: public Animal // inheriting Animal class.
{
public:
string color = "Grey";
};
int main(void) {
Animal d= Dog();
cout<<d.color;
}
Expected Output:-
Black
Difference between Compile time polymorphism and run time polymorphism |