POLYMORPHISM
POLYMORPHISM Polymorphism is the property of the same object to behave differently in different context given the same message
Compile Time and Runtime Polymorphism Compile time - Function Overloading, Operator Overloading Run time - Virtual function with inheritance
Pointer to Object Normal objects cannot help in achieving runtime polymorphism It is possible to achieve it only by setting or defining pointer to objects
Pointer to Object Class demo.. demo DemoObj; demo *ptrdemoobj; ptrdemoobj = & DemoObj; Look at the code above. Pointer to object is similar to other pointers with no difference
this Pointer The this pointer is a pointer to an object invoked by the member function. Following code explains the usage of this pointer. #include<iostream.h> #include<string.h> using namespace std; class person string name; int age; person(string tempname, int tempage) name = tempname; age = tempage; person elder (person otherperson) if(age>otherperson.age) return *this; //returning the invoking object else return otherperson; friend ostream & operator << (ostream & tempout, person & tempperson); ; ostream & operator << (ostream & tempout, person & tempperson) tempout << "The person "<<tempperson.name<<" is " << tempperson.age << " years old\n"; return tempout; void main() person Steve( Steve Waugh", 35); person Mark( Mark Waugh", 20); person bigbrother = Steve.elder(B); cout<<bigbrother; Output of the Programme The person Steve Waugh is 35 years old This example shows how *this returns the elder brothers object. Steve is an elder brother. So when we call Steve.elder(Mark) the function elder is called where the age of invoking the object is compared with the age of Mark s object. Steve s age is more. So *this is returned from the function.
Compatibility of Derived and Base Class Pointers Base class pointers can point to a derived class object No casting is required Look at the following code BaseClass BC; DerivedClass DC : public BaseClass; BaseClass *ptrbc; DerivedClass ptrdc; ptrbc = &BC; ptrdc = &DC; ptrbc = &DC; ptrdc = &BC; //pointer and content are of similar types //pointer and content are of similar types // this is done without any casting // not allowed
Compatibility of Derived and Base Class Pointers Though the derived class and base class pointers seem compatible, there are some points to note. 1. Base class pointer can be made to point to a derived class object, but is cannot access the original members of the derived class. It can only access base class members of the derived class object. The base class pointer can see only base class subobject embedded in the derived class object, not the additional part of the derived class. However, if any of the functions defined in derived class is defined as virtual in base class, the case will be different. 2. In no case, derived class object pointer is made to point to the base class. It can only be done by casting. i.e. ptrdc = &BC will give error, but ptrdc = (DC *) &BC will work. 3. The increment and decrement operator with base pointer types do not behave as expected. A base class pointer is always incremented as per the base class size and a derived class pointer is always incremented as per the derived class size. Hence, if a base class pointer is pointing to a derived class object, after increment, it might not point to the next derived object.
Subobject Concept Whenever a base class is inherited, the derived class contains the base class subobject In case of multiple inheritance, the derived class contains multiple subobjects. In case of multilevel inheritance, we have n subobjects in the class derived at n+1 level Compiler automatically manages the pointer to point to the respective subobject of the derived class in case of multiple or multilevel inheritance e.g. BaseClass1 BC1; BaseClass2 BC2; DerivedClass dc : public BaseClass1, BaseClass2; BaseClass1 *ptrbc1; BaseClass2 *ptrbc2; DerivedClass ptrdc; ptrbc1 = &DC; ptrbc2 = &DC; // point to subobject of BaseClass1 // point to subobject of BaseClass2
Base Class and Derived Class Member Functions It is possible to have two different member functions with the same name, one in base class and other in derived class The function in the derived class is known as overloaded function in this case It is analogues to having a global and a local variable with the same name So, if we refer to a function in derived class, the derived class function is executed It is interesting to check the case where the pointer to the base class is made to point to derived class, and the overloaded function is called using that pointer.
Base Class and Derived Class Member Functions #include<iostream.h> #include<string.h> using namespace std; class shape int linestyle; int fillcoller; void draw() cout<<"shape is drawn \n"; ; class circle:public shape int radius; int centerpointx,centerpointy; void draw() cout<<"circle is drawn \n"; ; void main() Output of program Shape is drawn Circle is drawn Shape is drawn shape someshape, *ptrshape; circle ring; someshape.draw(); ring.draw(); ptrshape = ˚ ptrshape->draw(); // When we use a base class pointer pointing to the derived class, the function called is from the base class, not from the derived class
Base Class and Derived Class Member Functions #include<iostream.h> #include<string.h> using namespace std; class shape int linestyle; int fillcoller; virtual void draw() cout<<"shape is drawn \n"; ; class circle:public shape int radius; int centerpointx; int centerpointy; void draw() cout<<"circle is drawn \n"; ; class rectangle:public shape int lefttopx; int lefttopy; int rightbottomx; int rightbottomy; someotherfunctin() //no draw defined here // nothing ; void main() shape someshape, *ptrshape; circle ring; someshape.draw(); ring.draw(); ptrshape = ˚ ptrshape->draw(); rectangle square; ptrshape = □ ptrshape->draw(); // this would still call draw() of shape chass Output of the Program Shape is drawn Circle is drawn Circle is drawn Shape is drawn Notice the Change. Now ptrshape->draw() calls draw() of circle, not shape. In base class the function is defined as virtual. The addition of word virtual makes the whole difference. When the statement ptrshape->draw() is exucuted, it looks at the content of the ptrshape at runtime, and then executes the draw() function which is appropriate. This is the difference when we use virtual function instead of normal function
Virtual Functions Virtual functions are special They are treated differently by the compiler The class has an additional storage requirement when at least one virtual function is defined inside it A table is created additionally to store pointers to all virtual functions available to all the objects of the class. The table is called virtual table A single pointer to the virtual table is inserted in all class objects when the class contains at least one virtual function This pointer is known as vptr Whenever an object of such class wants to execute a virtual function, the table is referred to and the appropriate function is called
Virtual Functions Few syntactical constraints on use of virtual functions: 1. The function name must be preceded by virtual keyword in the base class 2. The function in the derived class must have the same name as of the virtual function defined in the base class and the same prototype. If the prototype is different, the function is as good as overloaded function and not treated as virtual function 3. The function in the derived class need not be preceded by the virtual keyword. If it is preceded by virtual keyword, it makes no difference 4. If a function with the same name is not defined in the derived class, the original base class function is invoked. 5. The virtual function must be defined in the base class; it may have an empty body though
Virtual Functions 6. For any real problem solving case using virtual function, the class hierarchy should be present. The virtual functions are the members of the classes of the hierarchy. They are called using either pointer to the base class or a reference to base class. This implies that the functions must be members of base as well as derived class, respectively 7. Polymorphism is achieved only using pointers to the base class. It is not possible using objects. This known as static invocation of virtual function 8. Virtual constructors are not possible. Constructing a derived class object needs specific construction of the base class subobject within. Obviously, their constructors cannot be virtual. When an object is deleted, we may have virtual destructor for destroying them, so the delete <pointer> operation works for the object pointed to, irrespective of the pointer type. Here, it is allowed to provide deletion of the entire derived class object while pointed to by a base class pointer. (otherwise it would only delete base class subobject of the derived class). Anyway, it is a good idea to have virtual destructors to avoid memory leaks
Default Arguments to Virtual Functions #include<iostream.h> #include<string.h> using namespace std; class shape int linestyle; int fillcoller; virtual int draw(int size=100) cout<<"shape is drawn \n"; return size; ; class circle:public shape int radius; int centerpointx; int centerpointy; int draw(int size=200) cout<<"circle is drawn \n"; return size; ; void main() shape someshape, *ptrshape; circle ring; someshape.draw(); ring.draw(); ptrshape = ˚ // base class pointer pointing to derived class // object int drawsize = ptrshape->draw(); // now this draws a circle cout<<"draw size for circle using a base class pointer is : "<<drawsize<<endl; // displays 100 instead of 200 circle *ptrcircle = ˚ //now we are using a derived class //pointer to point to a derived class // object drawsize=ptrcircle->draw(); cout<<"draw size for circle using a base class pointer is : "<<drawsize<<endl; // display 200 correctly Output of the program Shape is drawn Circle is drawn Draw size for circle using a base class pointer is 100 Circle is drawn Draw size for circle using a derived class pointer is 200
Default Arguments to Virtual Functions When we use a base class pointer to point to a derived class object, the default value of the base class function will be taken. Here the function which is executed is correct, but with the default value of the base class. On the other hand when we use a derived class pointer, the default value of derived class function is taken. Therefore, we cannot use default parameters like a normal function here. The solution here is to provide a local variable initialized in both such classes and then to use default argument. If we need default arguments, they can be assigned values of such local variables as in the next example. We only need to see if the user has supplied a value, if not, the user has supplied a value by keeping a dummy default argument. If the argument equals to the dummy argument, the use has not supplied a value and we can take the value from the local variable.
Default Arguments to Virtual Functions #include<iostream.h> #include<string.h> class shape int linestyle; int fillcoller; virtual int draw(int size=1) cout<<"shape is drawn \n"; int shapesize = 100; if(size==1) // default case size = shapesize; return size; ; class circle:public shape int radius; int centerpointx; int centerpointy; int draw(int size=1) cout<<"circle is drawn \n"; int circlesize = 200; if(size==1) // default case size = circlesize; return size; ; void main() shape someshape, *ptrshape; circle ring; someshape.draw(); ring.draw(); ptrshape = ˚ // base class pointer pointing to derived class //object int drawsize = ptrshape->draw(); // now this drows a circle cout<<"draw size for circle using a base class pointer is : <<drawsize<<endl; circle *ptrcircle = ˚ // now we are using a derived class //pointer to point to a derived class object drawsize=ptrcircle->draw(); cout<<"draw size for circle using a derived class pointer is : <<drawsize<<endl; Output of the Program Shape is drawn Circle is drawn Circle is drawn Draw size for circle using a base class pointer is : 200 Circle is drawn Draw size for circle using a derived class pointer is : 200
Using Virtual Functions #include<iostream.h> #include<ctime.h> class figure; // forward declaration class point int x,y; point(int tempx=0, int tempy=0) x = tempx; y = tempy; int getx() const return x; int gety() const return y; friend ostream & operator << (ostream & tempout, point & temppoint); ; ostream & operator << (ostream & tempout, point & temppoint) tempout<< "( "<<temppoint.getx() << ", "<< temppoint.gety() << " )"; return tempout; ; class shape point position; int color; virtual void draw() cout<<" Shape is drawn "; friend figure; // frgure is going to use parameters for drawing shapes ; class square:public shape point leftbottom; int length; square(point templeftbuttom, int templength) leftbottom = templeftbuttom; length = templength; void draw() cout<<"square is drawn at " << leftbottom << " and with length as " << length <<endl;
Using Virtual Functions class triangle:public shape point Avertex, Bvertex, Cvertex; triangle(point tempavertex, point tempbvertex, point tempcvertex) Avertex=tempavertex; Bvertex=tempbvertex; Cvertex=tempcvertex; void draw() cout<< "Triangle is drawn at " << Avertex <<" " <<Bvertex << " " << Cvertex << endl; ; class circle:public shape point center; int radius; circle(point tempcenter, int tempradius) center = tempcenter; radius = tempradius; void draw() cout<< "Circle is drawn at " << center <<" and the radius is " << radius << endl; ; class figure shape * images[25]; figure() srand( (unsigned)time( NULL ) ); for(int i=0; i<25; i++) int randomvalues[6]; for(int j=0; j<6; j++) randomvalues[i] = rand() % 50; point position1(randomvalues[0], randomvalues[1]); point position2(randomvalues[2], randomvalues[3]); point position3(randomvalues[4], randomvalues[5]); switch(int choice = rand() % 3) case 0: int length = rand() % 20; images[i] = new square(position1, length); break;
Using Virtual Functions ; case 1: images[i] = new triangle(position1, position2, position3); break; case 2: int radius = rand() % 10; images[i] = new circle(position1, radius); break; default: cout<< choice << " is a wrong choice "; void draw() for (int i=0;i<25;++i) images[i]->draw(); void main() figure myfigure; myfigure.draw(); Output of the Program Square is drawn at ( 27, 1244768 ) and with length as 11 Triangle is drawn at ( 27, 0 ) ( 0, 30 ) ( 1244896, 0 ) Triangle is drawn at ( 27, 0 ) ( 48, 30 ) ( 1244896, 0 ) Square is drawn at ( 27, 0 ) and with length as 2 Circle is drawn at ( 27, 0 ) and the radius is 0 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Square is drawn at ( 27, 0 ) and with length as 13 Square is drawn at ( 27, 0 ) and with length as 17 Circle is drawn at ( 27, 0 ) and the radius is 4 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Square is drawn at ( 27, 0 ) and with length as 19 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Circle is drawn at ( 27, 0 ) and the radius is 5 Circle is drawn at ( 27, 0 ) and the radius is 2 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Circle is drawn at ( 27, 0 ) and the radius is 8 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Square is drawn at ( 27, 0 ) and with length as 15 Square is drawn at ( 27, 0 ) and with length as 5 Square is drawn at ( 27, 0 ) and with length as 4 Triangle is drawn at ( 27, 0 ) ( 48, 13 ) ( 2, 1 ) Circle is drawn at ( 27, 0 ) and the radius is 6
Pure Virtual Function When a class does not have any object, there is no need to have functions for it as there is no object to utilize those functions Consider a case of class Shape that we have discussed earlier We will never have an object of class Shape in actual working environment If we define draw() in the class, we will never be able to execute it It is important to understand that, because draw is a virtual function, we cannot have polymorphism without draw() defined in class Shape In other words, we need the definition of draw() in the Shape class but do not need the body. We can do the same by writing virtual void draw() // Empty The same can also be achieved by writing virtual void draw() = 0; Here, when we write = 0 in place of the function body, the function is said to be pure virtual function
Pure Virtual Function Pure virtual function has an important advantage It forces the native class to be abstract, and object of the class cannot be created. In a way, when a programmer wants the user not to define objects of a specific class, he can define a pure virtual function (may be a dummy) inside the class.
//PureVirtualFunction.cpp #include<iostream.h> using namespace std; class shape int linestyle, fillcollor; virtual void draw()=0; // this is pure virtual function virtual ~shape() cout << "Shape destroyed... "<<endl; ; void shape::draw() cout << "Shape is drawn \n"; class circle:public shape int radius, centerpointx, centerpointy; ~circle() cout<<"circle destroyed...\n"; void draw() cout << "Circle is drawn \n"; ; void main() // shape someshape; // error:cannot instantiate // abstract class shape *ptrshape; circle ring; // someshape.draw(); // not allowed ptrshape->shape::draw(); //static call to shape draw ptrshape = ˚ ptrshape->draw(); // this draws a circle cout <<" Messages from destructor starts now\n\n"; ptrshape = new circle; delete ptrshape; // if ~shape is not virtual, this won't // work as expected cout<<" Above messages are from delete \n\n"; cout<<"following messages are normal messages exiting the program\n"; Output of the program Shape is drawn Circle is drawn Messages from destructor starts now Circle destroyed... Shape destroyed... Above messages are from delete following messages are normal messages exiting the program Circle destroyed... Shape destroyed...
?