Monday, April 30, 2012

POINTERS, VIRTUAL FUNCTIONS AND POLYMORPHISM

• Polymorphism simply means ‘one name multiple forms.’

ACHIEVING POLYMORPHISM COMPILE TIME POLYMORPHISM


The overloaded member functions are selected for invoking by matching arguments, both type and member. The compiler knows this information at the compile time and therefore, compiler is able to select the appropriate function for a particular call at the compile time itself. This is called early or static binding or static linking. It means that an object is bound to its function call at compile time.

RUN TIME POLYMORPHISM

An appropriate member function is selected while the program is running. C++ supports run-time polymorphism with the help of virtual functions. It is called late or dynamic binding because the appropriate function is selected dynamically at run time. Dynamic binding requires use of pointers to objects and is one of the powerful features of C++.
For e.g.
class A
{ int x;
public:
  void show() {….}  
  //show() in the base class
};
 Class B:public A
{ int y;
public:
void show() {…} 
// show() in derived class
};

Since, the prototype of show() is the same in both the places, the function is not overloaded and therefore static binding does not apply. (Class resolution operator can be also used). But, it would be nice if the appropriate member function could be selected while the function could be selected while the program is running. This is known as run time polymorphism and is achieved through virtual functions.

POINTERS

1. A pointer is a derived data type that refers to another data variable by storing the variable’s memory address rather than data.
2. A pointer variable defines where to get the value of a specific data variable instead of defining the actual data.
3. Pointers provide an alternative approach to access other data objects.
NOTE: we can locate asterisk (*) immediately before the pointer variable, or between the data type and the pointer variable, or immediately after the data type. It does not cause any effect in the execution process.
e.g.
#include<iostream.h>
#include<conio.h>
void main()
{ int a,*ptr1,**ptr2; 
//double pointer refers to memory address of the pointer
 clrscr();
 a=10;
 ptr1=&a;
 ptr2=&ptr1;
 cout<<"address of a:"<<ptr1<<endl;
 cout<<"address of ptr2:"<<ptr2<<endl;
 cout<<'after incrementing address values \n";
 ptr1=ptr1+2; // 204+2*2bytes
 ptr2=ptr2+2;
 cout<<"address of a:"<<ptr1;
 cout<<"\n address of ptr1":ptr2;
 cout<<"the value of a:"<<a;
 (*ptr1)=(*ptr1)/2;
 cout<<"\n value of a after calculation is"<< a; 
 //here a prints the value of *ptr
}

OUTPUT:
address of a:204
address of ptr1:306
after incrementing address values:
address of a:208
address of ptr1:310
the value of a is 10
value of a after calculation is 5
4. Void pointers, also known as generic pointers refer to variables of any data type. Before using void pointers, we must type cast the variables to the specific data types that they point to.
5. The pointers, which are not initialized in a program, are called null pointers. Pointers of any data type can be assigned with one value i.e. , ‘0’ called null address.
6. A pointer can be manipulated with the indirection operator i.e. ‘*’, which is also known as dereference operator.
7. Dereferencing a pointer allow us to get the content of the memory location that the pointer points to. Using the dereference operator, we can change the contents of the memory location.
8. Before dereferencing a pointer, it is essential to assign a value to the pointer. If we attempt to dereference an uninitialized pointer, it will cause runtime error by referring to any other location in memory.
9. C++ allows pointers to perform the following arithmetic operations: • A pointer can be incremented (++) (or) decremented (--).
• Any integer can be added to or subtracted from a pointer.
• One pointer can be subtracted from another.
For e.g.
#include<iostream.h>
#include<conio.h>
void main()
{int num[]={56,75,22,18,90};
 int *ptr,i;
 clrscr();
cout<<"the array values are:\n";
for(i=0;i<5;i++)
  cout<<num[i]<<"\n";
ptr=num;
cout<<"\n value of ptr:"<<*ptr;
ptr++;
cout<<"\n value of ptr++:"<<*ptr;
ptr--;
cout<<"\n value of ptr--:"<<*ptr;
ptr=ptr+2;
cout<<"\n value of ptr+2:"<<*ptr;
getch();
}

OUTPUT:
the array values are:56,75,22,18,90
value of ptr:56
value of ptr++:75
value of ptr--:56
value of ptr+2:22
10. Pointers are useful to allocate arrays dynamically, i.e., we can decide the array size at run time.
11. There is no error checking of array bounds in C++. Suppose we declare an array of size 25. The compiler issues no warnings if we attempt to access 26th location. It is the programmer’s task to check the array limits.
12. The array of pointers represents a collection of addresses. It points to an array of data items. Each element of the pointer array points to the element of the data array. Data items can be accessed either directly or by dereferencing the elements of pointer array.
13. char num[]=”one”; //creates an array of 4 characters which point to ‘o’, ’n’, ’e’,’\0’.
const char * numptr=”one”; // generates a pointer variable, which points to the first character, i.e., ‘o’ of the string.
14. The concept of pointer to function acts as a base for pointers to members.
15. The pointer to function is known as callback function.
16. Using function pointers, we can allow a C++ program to select a function dynamically at run time.
17. The function pointers cannot be dereferenced.
18. C++ provides 2 types of function pointers:
• Function pointers that point to static member functions
• Function pointers that point to non-static member functions. (require hidden arguments)
• Both are incompatible with each other.
SYNTAX: data-type(*function_name)();
For e.g.
#include<iostream.h>
typedef void(*funptr)(int,int);
void add(int i,int j)
{ cout<<i<<"+"<<j<<"="<<i+j;
}
void subtract(int i,int j)
{ cout<<i<<"-"<<j<<"="<<i-j;
}
int main()
{ funptr ptr;
  ptr=&add;
  ptr(1,2);
  ptr=&subtract;
  ptr(3,2);
  return 0;
}

OUTPUT:
1+2=3
3-2+1

POINTERS TO OBJECTS

1. Object pointers are useful in creating objects at the run time.
2. Member functions of a class can be referenced in 2 ways:
• Using dot operator and object x.getdata(100,2), or x.show();
• Using arrow operator and object pointer Ptr->getdata(100,2) or ptr->show(); We can also use this method, (*ptr).show(); //here *ptr is an alias for x.
[The parenthesis is necessary because the dot operator has higher precedence than the indirection operator *]
Syntax: class-name *pointer-name=&object
3. We can also create objects using pointers and new operator as follows: Item *ptr=new item; //item is a class-name This statement allocates enough memory for the data members in the object structure and assigns the address of the memory space to ptr.
4. If a class has a constructor with arguments and does not include an empty constructor, then we must supply the arguments when the object is created.
5. We can also create an array of objects using pointers.
For e.g. , item *ptr=new item[10]; //array of 10 objects creates memory space for an array of 10 objects of item. Remember, in such cases, if the class contains constructors, it must also contain an empty constructor.
For e.g.:
#include<iostream.h>
class item
{ int code;
  float price;
public:
 void getdata(int a,float b)
{ code=a; price=b;}
void show(void)
{cout<<code<<price;}
};
const int size=2;
int main()
{ item *p=new item[size];
  item *d=p;
  item x,i; 
  float y;
for(i=0;i<size;i++)
{ cin>>x>>y;
  p->getdata(x,y);
  p++;
}
for(i=0;i<size;i++)
{ d->show();
  d++;
}
return 0;
}

OUTPUT:
40 500
50 600
code:40
price:500
code:50
price:600

POINTERS TO DERIVED CLASS

• Pointers to objects of a base class are type-compatible with pointers to objects of a derived class. Therefore, a single pointer variable can be made to point to objects belonging to different classes.
• A base pointer, even when it is made to contain the address of a derived class, always executes the function in the base class. The compiler simply ignores the contents of the pointer and chooses the member function that matches the types of the pointer.
• Although, a base pointer can be made to point to any number of derived objects, it cannot directly objects, it cannot directly access the members defined by a derived class.

VIRTUAL FUNCTIONS


• When we use the same function name in both the derived and base classes, the function in base class is declared as virtual using the keyword virtual preceding its normal declaration.
• When a function is made virtual, C++ determines which function to use at run time based on the type of object pointed to by the base pointer, rather than the type of the pointer. Thus, by making a base pointer. Thus, by making the base pointer to point to different objects, we can execute different versions of the virtual functions.
• To achieve run- time polymorphism, we must access virtual functions through the use of a pointer declared as a pointer to the base class.
For e.g.:
#include<iostream.h>
class base{
public:
void display()             
// display is not virtual.Hence, called both the times
{ cout<<"display base";}
virtual void show()
{cout<<"show base";}
};
class derived
{ public:
  int d;
void display()
{cout<<"display derived";}
void show()
{cout<<"show derived";}
};
int main()
{ base B; 
  derived D;
  base *ptr; bptr=&B;
  bptr->display();  //calls base version
  bptr->show(); //calls base version
  bptr->d;  //does not work
  bptr=&D;
  bptr->display(); //calls base version
  bptr->display(); //calls derived version
return 0;
}

OUTPUT:
display base
show base
display base
show derived

PURE VIRTUAL FUNCTIONS

• A virtual function, equated to zero is called a ‘pure virtual function’ or ‘do-nothing’ functions. They only work as a placeholder.
• It is a function declared in a base class that has no definition related to the base class.
• A class containing such pure function is called an abstract class.

RULES FOR VIRTUAL FUNCTIONS

1. Must be members of some class.
2. Cannot be static members.
3. Accessed by using object pointers.
4. Can be a friend of another class.
5. It must be defined in a base class, even though it may not be used.
6. The prototypes of the base class version of a virtual function and all the derived class versions must be identical. If two functions with the same name have different prototypes, C++ considers them as overloaded functions, and the virtual function mechanism is ignored.
7. We cannot have virtual constructors, but we can have virtual destructors.
8. While a base pointer can type of the derived object, the reverse is not true. That is to say, we cannot use a pointer to a derived class to access an object of the base type. (this is because, base class pointer is compatible to both base and derived class, whereas the reverse is not true)
9. When a base pointer points to a derived class, incrementing or decrementing will not make it to point to the next object of the derived class. It is incremented or decremented only relative to its base type. Therefore, we should not use this method to use this method to move the pointer to the next object.
10. If a virtual function is defined in the base class, it need not be necessarily redefined in the derived class. In such cases, calls will invoke the base function.
NOTE:
If in the previous example, no inheritance was done, then bptr=&D would have shown a matching error or a compatibility error. Since, inheritance had been done, so no error takes place. Instead, it works as a void pointer and because of generic programming, both the time, shows the display function of the base class.

THIS POINTER

1. A ‘this pointer’ refers to an object that currently invokes a member function.
For example, the function call a show() will set the pointer ’this’ to the address of the object ‘a’. The starting address is the same as the address of the first variable in the class structure.
2. This keyword is basically used to represent an object that invokes a member function.
3. This unique pointer is automatically passed to a member function when it is called. The pointer this acts as an implicit argument to all the member functions.
For example:
class ABC 
{ int a;
…
..};
a=123;  this -> a=123;
APPLICATIONS
1. Implicit used while overloading the operators using member function.
• When a binary operator is overloaded using a member function, we pass only one argument to the function. The other argument is implicitly passed using the pointer ‘this’. 2. returns the object it points to return * this; inside a member function will return the object that invoked the function. It is important, when we want to compare two or more objects inside a member function and return the invoking object as a result. For e.g.
person & person::greater(person & x)
{ if(x.age>age)
  return x;
else
return *this;
}
Is invoked by function call as max=A.greater(B);
The above function returns object B, if age of person A. else, it will return object A, i.e. invoking object using pointer this.