Skip to main content

Polymorphism

You hear the term Polymorphism too frequently with object oriented languages. Along with Inheritance and Encapsulation, polymorphism is one of the corner stones of object oriented design. What is Polymorphism, exactly?

Polymorphism is a mechanism by which you provide single interface for multiple methods. (poly - many, morph - form).

In C++, polymorphism can be compile time or run-time.

Compile time polymorphism is provided with the help of overloaded operators/functions and templates.

Run time polymorphism is provided with the help of virtual functions and late binding.

Late Binding:

Connecting a function call to function body is called binding. Most functions use early binding where this binding happens before the program is run - during compile time. This is also called static binding.

Late binding (also called dynamic binding)  is when a function call is connected to function body at run time. This is done after looking at the type of the object. Late binding is achieved in C++ using the key word "virtual" with function declaration.

Virtual functions:


To use polymorphism, you need to declare base class function as virtual. Once it is declared as virtual and a derived class object is accessed using base class pointer or reference, the virtual function call will invoke most derived class function instead of the base class function.

Let us look at an example.
class Shape
{
public:
 virtual void draw();
};
void Shape::draw()
{ 
   cout<<"Drawing a shape ";
 }
class Circle:public Shape
{
public:
 void draw();
};
void Circle::draw()
{ 
    cout<<" Drawing a circle ";
 }
class Rectangle:public Shape
{
public:
 void draw();
};
void Rectangle::draw()
{
    cout<<" Drawing a rectangle ";
 } 
int main()
{
 Shape *ptr = new Rectangle;
 ptr->draw();
 delete ptr;
 ptr = new Circle;
 ptr->draw();
 delete ptr;
 ptr = new Shape;
 ptr->draw();
 delete ptr;
}

Output of this program is

Drawing a rectangle Drawing a circle Drawing a shape

Though ptr is a pointer to Shape class, ptr->draw() does not call Shape class draw() function always. Instead it calls draw function of Rectangle class or Circle class.

draw() which is a virtual function, calls different functions- first it calls Rectanlge class draw() function when ptr is pointing to a rectangle. Next it calls Circle class draw()function when ptr is pointing to a circle etc.

As the call binding happens at run time, the system finds out the type of object(class) ptr is pointing to and calls the corresponding draw() function.

Let us look at another example here.

#include <iostream>
using namespace std;
class polygon
{
public:
 virtual void printNS()
 { cout<<"Num of sides is unknown ";}
};
class Rectangle:public polygon
{
public:
 virtual void printNS()
 { cout<<"Num of sides is "<<4;}
};
class Triangle:public polygon
{
public:
 virtual void printNS()
 { cout<<"Num of sides is "<<3;}
};

void printPolygon(polygon &p)
{
 p.printNS();
}
int main(int argc, char **argv)
{
 Triangle t;
 Rectangle r;
 polygon p;
 printPolygon(t);
 printPolygon(r);
 printPolygon(p);
}

Output
Num of sides is  3 Num of sides is  4 Num of sides is unknown

In the program above, printPolygon() accepts a polygon reference as parameter. And as derived class object  IS A  a base class object, the parameter to printPolygon() function can be either a polygon  or it can be a triangle or a rectangle.

In the program, this function is called with a triangle, a rectangle and a polygon.

printPolygon() calls printNS() function. When argument to printPolygon() is t - a triangle, printNS() of triangle class is called by the function. When it is a rectangle, rectangle printNS() is called.

Exercise: What happens when you remove virtual keyword from printNS() function in base class?

How is this polymorphism implemented in C++?

It is implemented with the help of a pointer called VPTR and a virtual table - VTable.

Virtual Table and vptr


Every class in C++ with at least one virtual function will have table called VTABLE which has names and addresses of every virtual function of that class.

Also, every object of a class which has a virtual function will have a hidden data member called vptr. vptr points to the VTABLE of the class. When an object is created, vptr will initialized with address of VTABLE of its class. e.g. If the object belongs to Circle class, the vptr points to vtable of Circle class.

When a virtual function is called, vptr is used to get address of VTABLE. Using VTABLE address of virtual function is obtained. And then this function is invoked.

Let us add few functions to the first program with shape, rectangle and circle used earlier.
void printShape(Shape s)
{
 s.draw();
}
void printShapeRef(Shape &s)
{
 s.draw();
}
int main()
{
 Shape s ; Circle c;
 printShape(c);//prints Drawing a shape
 printShapeRef(c);//prints Drawing a circle
}

Output
Drawing a circle
Drawing a shape

For the printShape function, the type of parameter is Shape. So the function binding happens at compile time and there will not be polymorphism.The base class function will be invoked.

But for printShapeRef() function, the parameter is a reference. So it could be a shape or any of its derived class object (because a circle is a Shape, Rectangle is also a Shape). So, the function binding is deferred until runtime. During code execution, the s.draw() gets vptr of argument s. Using vptrVTABLE is obtained and using VTABLE, address of draw() function is obtained and it is called. 

So when printShapeRef is called with a circle object, vptr points to VTABLE of circle class. From this table, draw() function of circle class is obtained, draw() of circle is called. But when argument to printShateRef() is a shape object, vptr points to VTABLE of shape class. So draw() of shape class is called.

Exercise:
    Find the size of object which has at least one virtual function. What do you observe?

Note:
  1. Virtual function can not be static
  2. Constructor can not be virtual but destructor can be virtual.
  3. Virtual function overridden in derived class, must have the exact same signature as base class function.
  4. Polymorphism is not exhibited when there is direct object of derived class. It is exhibited only with pointers and references.
 Virtual Destructor


A class CAN NOT have a virtual constructor. But it can have a virtual destructor.

In fact a virtual destructor is desirable in certain cases. Let us look at an example which shows us the need for virtual destructor.
#include<iostream>
using namespace std;
class A
{
public:
 A(){cout<<"A constructr";}
 ~A(){cout<<"A destructor";}
};
class B:public A
{
public:
 B(){cout<<"B constructr";}
 ~B(){cout<<"B destructor";}
};
int main()
{
 A *ptr = new B;
 delete ptr;
}

What do you expect the output to be? 

A constructorB constructorB destructorA destructor

Sorry. That does not happen. Since ptr is A class pointer, delete ptr will only call A destructor. So the derived class destructor is not called at all.

Actual Output
A constructorB constructorA destructor

That is okay as long as derived class does not need any cleaning up. But if we have dynamic variables, files, threads etc, we have to ensure that derived class destructor is called. How do we ensure that derived class destructor is also called - even when a base class pointer is allocated to derived class object?

What if we make  base class destructor as virtual?  when a pointer to base class which is pointing to derived class object is being destroyed, the run time system will call derived class destructor because of polymorphism. But derived class destructor always calls base class destructor. So both base class as well as derived class destructors will get called.

Let us replace the destructor of A class in the previous example.
class A
{
public:
/***code****/
 virtual ~A(){cout<<"A destructor";}
}; 
class B:public A
{ /*code*/
};
int main()
{
 A *ptr = new B;
 delete ptr;
} 

Now the output will be
A constructorB constructorB destructorA destructor

Comments

Popular posts from this blog

Ten questions in C/C++

Let us see some questions in C and C++ Write printf statement in C to print - I got 98% in Maths Can you execute a function before main() in C? If yes, how is it done? Can you write a program to find if a number is even without using modulo operator? How do you define a data member which is common to all objects of a class in C++? Can we have a single constructor for a class, but still create objects from the class passing zero/one and two parameters? What problems might occur if a class has no default constructor? Is the following statement correct? fprintf(stdout,"Hello world");   Why do we use the following statement in C++ program? using namespace std;  Can you write a single statement to check if the number is a power of 2?  What does the following statement mean in C/C++?4 if(a) b++;    So we have 10 questions. How many of these can you answer?   Do you need more questions in C and C++?    You can find t...

It is a constant

In good old days, C programmers would use preprocessor directive to define constants. e.g. #define s 10 But we know now that, as compiler never gets to see these, preprocessor statements are error prone. Hence we have const s. A constant - defined with keyword const promises that this entity is never going to change. And if we accidentally modify a const, compiler throws an error. Let us look at an example. #include<iostream> using namespace std; int main () { int a = 10 ; const int b = 12 ; a ++ ; b = 18 ; } When we compile this program, compiler tells us that default.cpp: In function ‘int main()’: default.cpp:8:7: error: assignment of read-only variable ‘b’      b = 18; So it is catching the error that we are trying to modify a const. Whenever a local variable or parameter need not be modified, declare it as a const.  Yes, we can make even parameters as constant. We can make objects constant or even me...

Abstract class

 If we can not create any objects of a class, then it is called an abstract class. A class is made abstract by adding at least one pure virtual function to it. Pure virtual function A function is said to be a pure virtual function , if it has no definition but has only declaration. Pure virtual function is defined with the keyword virtual and followed by return type, function name and "=0". class Shape { public: virtual void printarea() =0 ; }; int main () { Shape obj1; //error } Here printarea() function of Shape class is a pure virtual function as it has no body. To make a function as pure virtual function, you should use =0 at the end of virtual function declaration Abstract class When a class has at least one pure virtual function, it is incomplete and no objects can be created from that class. Such a class is called an abstract class . In the earlier example class Shape is an abstract class, and objects can not be created from t...