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
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
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 54. 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:2210. 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
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
• 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
• 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
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
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.
No comments:
Post a Comment