Friday 31 May 2013

Virtual function in C++


Object Pointers
Just like any ordinary pointer of any primitive data type, we may have object pointers that point to some object.

Example:
#include<iostream.h>
#include<conio.h>
class complex
{
      private:
              int a,b;
      public:
             void getdata()
             {
                  cout<<"Enter real and img part";
                  cin>>a>>b;
             }
             void show()
             {
                  cout<<"\na="<<a<<" b="<<b;
             }
};
int main()
{
     complex c1;
     complex *ptr;
     ptr=&c1;
     ptr->getdata();
     ptr->show();
     getch();
}

In the above example,
l  We created an object of type complex and also declared a pointer of type complex. Name of pointer is ptr which is object pointer as it can point to an object of type complex.
l  Pointer ptr stores address of object c1.
l  When ever members of object are accessed using object name we need to use dot (.) operator, but if members are to be accessed through pointer name we need to use arrow (->) operator.
l  In the line ptr->getdata(), function getdata() will work for the object pointed to by ptr.


Dynamic Memory allocation: new and delete

Run time memory can be allocated by using keyword new and this allocated memory can be released using keyword delete.

Example
#include<iostream.h>
#include<conio.h>
void main()
{
     clrscr();
     int *p= new int;
     float *q= new float;
     *p=5;
     *q=31.45;
     cout<<”*p=”<<*p;
     cout<<”*q=”<<*q;
     delete p;
     delete q;
     getch();
}

In the above program
l  Pointer p is used to store address of dynamically allocated int type block using keyword new.
l  Pointer q is used to store address of dynamically allocated float type block using keyword new.
l  In C language we dynamically allocate memory using predefined functions malloc() or calloc(), here same functionality is done by keyword new.
l  The only difference between malloc and new is in former case we have to specify the size of block and in later case we have to mention name of data type.
l  Dynamically allocated memory can be accessed only through pointers.
l  Memory can be released using keyword delete, this is same as function free().
l  Notice that delete releases the memory for specified address. delete p; would not mean that p is released instead it means delete releases memory pointed to by p.

Example
#include<iostream.h>
#include<conio.h>
void main()
{
     clrscr();
     int *p= new int[4];
     *p=5;
     *(p+1)=12;
     *(p+2)=33;
     *(p+3)=45;
     for(int i=0;i<=3;i++)
          cout<<”*(p+”<<i<<”)=”<<*(p+i);
    
delete []p;
     getch();
}

In the above example
l  We created an array of 4 int blocks dynamically using keyword new.
l  Notice square brackets after data type int. This is used to mention sized of dynamic array.
l  Address of the first block of array gets stored in p. Subsequent instructions are used to store values in four blocks of array.
l  At last we use delete to release memory, see its syntax carefully, square brackets needs to be mentioned before pointer name. It makes possible to release memory of all four blocks.

Example
#include<iostream.h>
#include<conio.h>
class complex
{
      private:
              int a,b;
      public:
             complex() {a=0; b=0;}
             void getdata()
             {
                  cout<<"Enter real and img part";
                  cin>>a>>b;
             }
             void show()
             {
                  cout<<"\na="<<a<<" b="<<b;
             }
};
void main()
{
     clrscr();
     complex *p= new complex;
     p->show();
     p->getdata();
     p->show();
     delete p;
     getch();
}

In the above example
l  We created object pointer to store address of dynamically allocated memory for object of type complex.
l  As soon as object is created constructor is called.
l  p->show()  display the values of a and b of this run time generated object.
l  Subsequent instructions are used to call other members of the class.
l  Keyword delete releases the memory of object pointed to by p.

The this Pointer

l  C++ provides a special in-build pointer know as this pointer.
l  this pointer is a local pointer variable in every member function.
l  this pointer needs no declaration
l  Its scope is limited to the member function.
l  Friend functions are not member functions thus they do not have this pointer.
l  Static member functions do not have this pointer
l  this pointer is of type of that class of which function is a member.

Example

#include<iostream.h>
#include<conio.h>
class complex
{
      private:
              int a,b;
      public:
             void getdata()
             {
                  cout<<"Enter real and img part";
                  cin>>a>>b;
             }
             void show()
             {
                  cout<<"\na="<<a<<" b="<<b;
             }
             complex sumgreater(complex c)
             {
                     if((a+b) > (c.a+c.b))
                         return(*this);
                     else
                         return(c);
             }
};
int main()
{
    complex c1,c2,c3;
    c1.getdata();
    c2.getdata();
    c3=c1.sumgreater(c2);
    c3.show();
    getch();
}    

  In the above example
lObserve the member function sumgreater(), its job to return an object whose sum of member variable is greater among two objects.
lIn the line c3=c1.sumgreater(c2); sumgreater() function is called by object c1, thus the address of c1 is passed implicitly to the member function sumgreater(), which is then received by pointer this.
lWe also passes an object c2 to function sumgreater(), which is received in object c.
lIf the sum of data stored in a and b of c1 is greater than the sum of data stored in a and b of c2( represented by c in sumgreater()) then object c1 has to be returned. The problem is how to represent caller object c1 in function sumgreater().
lThe problem is solved by using this pointer, as this contains the address of c1, *this is used to represent caller object (that is c1 in our example).


Virtual Function

Before going through virtual function we need to understand few of the basic things.
In inheritance, let us assume there is a Base class and a Derived class. Now we create a pointer of base class in function main(). Considering this situation dive in for depth knowledge, read the following two points:

1)      Base class pointer can store address of base class object or can store address of Derived class object.
2)      If Base pointer contains address of Derived class object, we can access only those members of Derived class using Base pointer which are inherited from Base class.

Example
#include<iostream.h>
#include<conio.h>
class Base
{
     public:
          void fun1()
{
     cout<<”Base Class, fun1()”;
}
void fun2()
{
     cout<<”Base Class, fun2()”;
}
};

class Derived : public Base
{
     public:
          void fun1()
{
     cout<<”Derived Class, fun1()”;
}
void fun2()
{
     cout<<”Derived Class, fun2()”;
}
};
void main()
{
     clrscr();
     Base *p;
p= new Base;
     p->fun1();  //Base class version is called
            p->fun2(); //Base class version is called
            delete p;
     p= new Derived;
     p->fun1();  //Base class version is called
            p->fun2(); //Base class version is called
            delete p;
            getch();
}
           
If we call fun1() or fun2() using Object of Derived class using dot (.) operator, Derived class version of functions would be called. This is called function overriding (see chapter 9)

We focus our discussion to access member using object pointers only.

In the above example if we made base class version of function virtual by prefixing a keyword virtual then Base pointer calls version of function depends on address contained by Base pointer. If Base pointer contains address of Base class object then function of Base class version would be run and if Base pointer contains address of Derived class object then function of Derived class version would run.

Example
#include<iostream.h>
#include<conio.h>
class Base
{
     public:
          virtual void fun1()
{
     cout<<”Base Class, fun1()”;
}
void fun2()
{
     cout<<”Base Class, fun2()”;
}
};

class Derived : public Base
{
     public:
          void fun1()
{
     cout<<”Derived Class, fun1()”;
}
void fun2()
{
     cout<<”Derived Class, fun2()”;
}
};
void main()
{
     clrscr();
     Base *p;
p= new Base;
     p->fun1();  //Base class version is called
            p->fun2(); //Base class version is called
            delete p;
     p= new Derived;
     p->fun1();  //Derived class version is called
            p->fun2(); //Base class version is called
            delete p;
            getch();
}

Explanation
l  A virtual function is always a member of a class.
l  A function can be made virtual by using keyword virtual (see fun1() in Base class)
l  It usually has a different functionality in Derived class
l  A function call is resolved at run time
l  See in above example p->fun1() is written two times but they call different versions, former is a call to Base class version of fun1() and later is call to Derived class version. Since both the calls look alike it is polymorphism.
l  It is worth mentioning here that virtual function mechanism is valid for functions sharing same prototype but different definitions, one in Base class and other in Derived class. If functions have same name but vary in arguments, virtual keyword losses its effect.
l  If Base class contain any virtual function, it is not mandatory to redefine in Derived class.

The Virtual table

l  To implement virtual functions, C++ uses a special form of late binding known as the virtual table. The virtual table is a lookup table of functions used to resolve function calls in a dynamic/late binding manner. The virtual table sometimes goes by other names, such as “vtable”, “virtual function table”, “virtual method table”, or “dispatch table”.
l  Every class that uses virtual functions (or is derived from a class that uses virtual functions) is given it’s own virtual table.
l  This table is simply a static array that the compiler sets up at compile time.
l  A virtual table contains one entry for each virtual function that can be called by objects of the class. Each entry in this table is simply a function pointer that points to the most-derived function accessible by that class.
l  The compiler also adds a hidden pointer to the base class, which we will call *__vptr.
l  *__vptr is set (automatically) when a class instance is created so that it points to the virtual table for that class.
l  It makes each class object allocated bigger by the size of one pointer.
l  It also means that *__vptr is inherited by derived classes, which is important.
l  When a class object is created, *__vptr is set to point to the virtual table for that class. For example, when a object of type Base is created, *__vptr is set to point to the virtual table for Base.
l  When objects of type Derived is constructed, *__vptr is set to point to the virtual table for Derived
l  When these virtual tables are filled out, each entry is filled out with the most-derived function an object of that class type can call.



Virtual Destructor
l  Like constructors, destructors also non inheritable elements of class.
l  Remember, Constructors can not be made virtual, but destructors can be.
l  Although C++ provides a default destructor for your classes if you do not provide one yourself, it is sometimes the case that you will want to provide your own destructor (particularly if the class needs to deallocate memory). You should always make your destructors virtual if you’re dealing with inheritance.

Example (without virtual destructor)
#include<iostream.h>
#include<conio.h>
class Base
{     
      public:
       void fun1()
       { cout<<"\nYou are in class Base and fun1()"; }
       ~Base()
       { cout<<"You are in Base Destructor "; }
};
class Derived: public Base
{
      int a,b;
      public:
        void fun1() //function overriding
       { cout<<"\nYou are in class Derived and fun1()"; }
     
       ~Derived()
       { cout<<"You are in Derived Destructor "; }
};
int main()
{
    cout<<"You are in main()";
    {
      Base *p=new Derived;
      p->fun1();
      delete p;
    }
    getch();
}
Output:
You are in main()
You are in class Base and fun1() 
You are in Base Destructor

l  You can observe that only Base class destructor executes at the line delete p;
l  To execute Derived class constructor as well as Base class constructor you need to make destructor virtual.

Example (With Virtual destructor)

#include<iostream.h>
#include<conio.h>
class Base
{     
      public:
             void fun1()
             { cout<<"\nYou are in class Base and fun1()"; }
            virtual  ~Base()
             { cout<<"You are in Base Destructor "; }
};
class Derived: public Base
{
      int a,b;
      public:
             void fun1()     //function overriding
           { cout<<"\nYou are in class Derived and fun1()"; }
     
             ~Derived()
             { cout<<"You are in Derived Destructor "; }
};
int main()
{
      cout<<"You are in main()";
    {
      Base *p=new Derived;
      p->fun1();
      delete p;
    }
    getch();
}
Output:
You are in main()
You are in class Base and fun1()
 You are in Derived Destructor
You are in Base Destructor



Pure Virtual function

A function in a class can be made virtual by writing keyword virtual and if it has no definition it is called pure virtual function.
Pure virtual function is also known as do-nothing function.
Syntax:
            virtual return type function name()=0;

Notice the special way of assigning 0, it is actually not assignment but just to mention compiler that this function has no body that is no definition.

If a class is containing pure virtual function:
l  Class is known as abstract class, that is we can not create its object
l  To access member of this class we need to define its derived class since we can not create its object.
l  Derived class of abstract class must either redefine pure virtual function or re-declare it again pure virtual function.

Early and Late Binding

When a program is compiled, the compiler converts each statement in your C++ program into one or more lines of machine language. Each line of machine language is given it’s own unique sequential address. This is no different for functions — when a function is encountered, it is converted into machine language and given the next available address. Thus, each function ends up with a unique machine language address.

Binding refers to the process that is used to convert identifiers (such as variable and function names) into machine language addresses. Although binding is used for both variables and functions.

Early Binding

Early binding (also called static binding) means the compiler is able to directly associate the identifier name (such as a function or variable name) with a machine address.
When the compiler encounters a function call, it replaces the function call with a machine language instruction that tells the CPU to jump to the address of the function.
Those decisions taken at compile time saves execution time, but such programs are not flexible.
Function overloading and operator overloading are example of early binding.
Late Binding

In some programs, it is not possible to know which function will be called until runtime (when the program is run). This is known as late binding (or dynamic binding).

In C++, one way to get late binding is to use function pointers.

Those decisions taken at run time are taking extra execution time, but provide greater flexibility to the user.

Virtual function is an example of late binding.

No comments:

Post a Comment