Polymorphism in C++

New comers to OOPs language find this term confusing. What is polymorphism exactly?

poly - many morph - form(?)

So something which takes different forms is said to be polymorphic. ( Like they say bahurupiya in Hindi. ) We have all studied about carbon in chemistry. Carbon takes many forms such as charcoal, coal and diamond. That fact fold us about "maya" (illusion) of this world where a diamond is as worthless as charcoal. It did not. Not at that time.

Anyways, carbon can take many forms. So it is polymorphic element. What has that got to do with software?. OK. If an object in OOPs language can take many forms, that object is said to be polymorphic. 

Let us look at an example of shape class here. A shape is a vague term. So we can not know its area. Not easily.

e.g.

class shape
{ 
    int color;
  public:
    float area()
    { 
        float a=0;
        return a;
    }
     
}


Let us say we have an inherited class rectangle. If base class shape has area method as shown above, the sub-class rectangle may re-define this area method to return product of length by width.


class rectangle:public shape
{
    
public:
   float area()
   {
     return len*wid;
   }
}

area() method in the rectangle class is overriding shape class method.

Now let us look at main function



int main()
{
    shape s1,s2;
    cout<<s1.area();//prints 0- calls base class function
    rectangle r1;
    r1.set_len(3);
    r1.set_wid(5);//sets length of width of rectangle. Functions not shown here
    cout<<r1.area();//prints 15. calls derived class function
    shape *sptr1;  

    sptr1 = new rectangle();
    sptr1.set_len(4);
    sptr1.set_wid(5);
    cout<<sptr1->area();//will it call derived function or base ??
  } 

In the above program s1.area() prints 0 as it calls base class function. And r1.area() returns 15 as it calls sub-class function.

So far so good. But what about the pointer sptr1. sptr1 is a shape pointer, but it points to a rectangle object.  Every object of derived class is a base class object also. (ISA)  So when we say sptr1 = new rectangle(), we do not get any error. 

A rectangle is created and its address is assigned to sptr1. But if we call its area method, will the method return 20 by calling rectangle area  or  0 by calling shape area?

It returns 0. Though the object is rectangle, pointer is shape pointer. Hence instead of calling overriding function, base class function is called. Which is wrong.

In many situations, a base class pointer or reference will be pointing to any of the derived class objects. And the program must call the correct overriding function, instead of base class function. To achieve this we need to use virtual functions.


Virtual functions


If a base class function is declared as virtual, binding of function call with function definition is done at run time, instead of compile time. This is called late binding or dynamic binding. Ordinary functions use static binding where binding of function call with function definition is done at compile time.

Dynamic binding ensures that if a pointer of base class points to an object of derived class then derived class function is called. In our previous example, instead of area returning 0, it will return 20.

For dynamic binding to happen, we have to write the base class function as virtual as shown below
 

class shape
{
public:
    virtual float area()
    {
       return 0;
    }
} 


As we can see, the shape has defined area method as virtual. If base class function is virtual, derived class function also becomes virtual. Now pointer to shape pointing to rectangle, will call rectangle area method. This behavior is called polymorphism.


But how exactly does polymorphism happen? How does the system know which function to call? In C++, it is done using vptr and vtable.

VTables


Late binding is achieved with the help of vtable or virtual table and vptr - virtual pointer. When a an object is created from class with virtual function it will have an extra hidden data member called vptr. This vptr will point to the vtable for that object.

Vtable is a table consisting of addresses of virtual functions. Base class has its vtable and all derived classes will have their virtual tables.  If there are 3 virtual functions and 2 ordinary in a class then vtable will have 3 entries.

If a derived class overrides a virtual function, then its vtable's entry will point to  derived class function. If it does not, then  vtable entry for corresponding function will point to base class function.

If an object is created such as

Base *ptr = new Base();

Then vptr points to Base class vtable.

If instead object is crated using

Base *ptr = new Derived();

the object's vptr will point to derived class's vtable.


When a function is called in such a situation, the binding happens via vptr. Address is obtained using vtable then the function is called.




 As you can see from the diagram, the objects pointed by ptr2 and ptr3 are pointing to a vtable of Derived class where as object pointed by ptr1 is pointing to vtable of Base class. And you can also see that vtable consists of function pointers of virtual functions.

Few points to be noted
  • Polymorphisms or late binding is not displayed when object is used instead of pointer or reference.
 
     Base b;
     b.setn(10);
     b.print();
     Derived d;
     d.setn(20);
     d.print();
     b =d;
     b.print();


        Here b.print() will not call derived class functions at all.
  • The virtual key word  should be used inside the class declaration. If used outside the class, a syntax error is produced.
  • Late binding is used even when the object is called to function via reference parameter.
     
    
     void printObject(Base &base1)
     {
          base1.print();
     }
     int main()
     {
       Derived d;
       printObject(d);       
     }
Here printObject calls derived class print function.
  • A static function can not be virtual.
You can download this program from
a.cpp at google docs



Comments

Popular Posts