390 likes | 506 Views
VIRTUAL FUNCTIONS AND DYNAMIC POLYMORPHISM. Polymorphism refers to the property by which objects belonging to different classes are able to respond to the same message, but in different forms.
E N D
Polymorphism refers to the property by which objects belonging to different classes are able to respond to the same message, but in different forms. • An essential requirement of polymorphism is the ability to refer to the objects without any regard to their classes. • Therefore a single pointer is used to refer to the objects of different classes. • The compiler simply ignores the contents of the pointer and chooses the member function that matches the type of the pointer
long int factorialsum(int a , int b) { int I; long int sum; for(sum=0,i=a;i<=b;i++) sum+=factorial(i); return sum; } long int cubesum(int a , int b) { int I; long int sum; for(sum=0,i=a;i<=b;i++) sum+=cube(i); return sum; }
long int generatesum(int a , int b, long int (*p)()) { int I; long int sum; for(sum=0,i=a;i<=b;i++) sum+=(*p)(i); return sum; } • We call this function by passing the functions whose returned values have to be the summed up as the last parameter to this generic function. X=generatesum(1,5,factorial); X= geneatesum(2.3.cube);
(*p)(i) exhibits polymorphic behavior, because while compiling it, it is not known which functions will actually be executed. • It becomes apparent only when the client program that calls this generic function is compiled.
class A { public: void show() { cout<<"A";} }; class B:public A { public: void show() { A::show(); cout<<"B"; } }; void main() { B B1; A A1; A *Aptr; Aptr=&A1; Aptr->show(); Aptr=&B1; Aptr->show(); }
Output : AA The compiler decides which function to call by considering the type of the pointer, the type of the object pointed at by the pointer is not considered. In such cases, Overriding becomes ineffective!
class A { public: void show() { cout<<“A”; } }; class B:public A { public: void show() { A::show(); cout<<“B”; } }; void main() { B B1; A A1; A &Aref1=A1; Aref1.show(); A &Aref2=B1; Aref2.show(); } Output : AA
Virtual Functions • Provides the feature of Dynamic Polymorphism. • 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 (late binding). • Thus, by making the base pointer point to different objects, different versions of the virtual function can be executed.
How do you declare a function as virtual? • The prototype of the function within the class should be prefixed with the virtual keyword. • It may appear before or after the keyword specifying the return type of the function. • If defined outside the class, only function prototype needs to have virtual keyword. class A { public: virtual void show();}; class A { public: void virtual show();};
class A { public: virtual void show(); }; class B:public A { public: void show(); }; void B::show() { A::show(); cout<<"B"; } void A::show() { cout<<"A"; } void main() { B B1; A A1; A *Aptr; Aptr=&A1; Aptr->show(); Aptr=&B1; Aptr->show(); }
When the base class pointer points at an object of the derived class and a call is dispatched to an overridden virtual function, then it is the overriding function of the derived class that is called.( and not the overridden function in the base class) • Virtual Functions of the base class reappear as virtual in the derived class as well. (using the keyword virtual while defining the overriding derived class function is optional )
class A { public: virtual void show()//A::show() is virtual {cout<<“A”<<endl; } }; class B:public A { public: void show();{cout<<“B”<<endl;} }; //B::show() is virtual class C:public B { public: void show() {cout<<“C”<<endl;} }; //C::show() is virtual
void main() { B *Bptr; Bptr= new C; Bptr->show(); } Output : C
Run time polymorphism is achieved only when a virtual function is accessed through a pointer to the base class. • It cannot be achieved using the object name along with the dot operator to access virtual function.
The Mechanism of Virtual Functions: • For every base class with virtual functions, a table of function addresses is created during runtime.(VTBL or Virtual Table) • This table contains the address of every virtual function that has been declared in the corresponding class. Eg: class A { public: virtual void abc(); virtual void def(); };
Address of A::def() • During runtime, the VBTL of class A will be: • Such a table of addresses of virtual functions will be created for the derived class as well. • If the derived class does not redefine a certain base class virtual member, the table will contain the address of the inherited base class virtual function itself. ( else the table will contain the address of the overriding function.) 101 201 Address of A::abc()
class B: public A { public: void def(); void virtual ghi(); }; The VBTL for class B appears as: Address of B::def() 101 301 401 Address of A::abc() Address of B::ghi()
Every object of a class has a virtual function containing a pointer called a Virtual pointer or VPTR to the VTBL of the corresponding class. • Whenever a call is dispatched to a virtual function( through an object or reference to an object or pointer to an object), then the first of all the values of the VPTR of the object is read. • The address of the called function from the corresponding VTBL is obtained and called.
The table size varies from class to class. • The size of the objects of the classes with virtual functions increases uniformly due to the presence of VPTR. Why not declare functions virtual by default? • They incur a runtime cost in the form of space that is wasted for creating the VTBL and embedding the VPTR in each and every object of the base/derived class. • Time is also lost in searching the VTBL for the function address.
Pure Virtual Functions • The virtual function is seldom used for performing any task is generally used as a placeholder. • A do-nothing function may be defined as: virtual <r_type> <f_name> (<formal_args>) =0; Such a function is a pure virtual function. • A pure virtual function is a function declared in a base class that has no definition relative to the base class. • In such cases, the compiler requires it to either define the function or redeclares it • as a pure virtual function.
#include<iostream.h> class number { protected : int val ; public : void setVal (int i ){ val = i ; } virtual void show () = 0 ; }; class hexType : public number { public : void show () { cout<<hex<<val<<“\n” ; } }; class decType : public number { public : void show () { cout<<val<<“\n” ; } };
class octType : public number { public : void show () { cout<<oct<<val<<“\n” ; } }; int main() { decType d; hexType h; octType o ; d.setVal(20); d.show(); // displays 20 decimal h.setVal(20); h.show(); // displays 14 hex o.setVal(20); o.show(); // displays 24 decimal return 0; } When a virtual function is declared as pure, all derived classes must override it. If a derived class fails, a compile-time error will result.
A class containing a pure virtual function cannot be used to declare any objects of its own and are called ABSTRACT BASE CLASSES. • An ABC can not be instantiated, that is, the object of an ABC can not be declared. What is the purpose of an Abstract Base Class? • It is used to provide some traits to the derived classes. It is used as an interface • To create a base pointer required for achieving run time polymorphism.
Any class that derives from an ABC must override all pure virtual functions in the base class or it gets branded as an ABC by the compiler. • ABC are also used to build implementation in stages. If a pure virtual function inherited from the base class is not defined in the derived class, it remains a pure virtual function in the derived class. Thus, the derived class also becomes an abstract class. • If there is no concrete definition of few of the virtual functions of the base class, with respect to that derived class, then they will be left undefined and that class will become ABC for the further derived classes.
class A { public: void virtual abc()=0; void virtual def()=0; void virtual ghi()=0; }; void main() { A A1; //error }
class B: public A { public: void abc() { //deftn of abc} }; void main() { B B1; //error }
class C: public B { public: void def() { // deftn of def } void ghi() {//deftn of ghi} }; void main() { C C1; //OK }
class A { public: void virtual abc()=0; }; void A::abc() // pure virtual fn defined {//defn of A::abc} class B:public A { public: void abc(); }; void B::abc() { A::abc(); // derived class invokes that function //thro scope resolution operator //defn of rest of B::abc() } //defining a pure virtual function in the abstract base class itself
The ability to provide an implementation to a pure virtual function allows data types to provide core functionalities while still requiring the derived classes to provide specialized implementations. • The class remains abstract even if we provide an implementation for its pure virtual function. • As per requirement, a member function can be non-virtual, virtual or pure virtual.
Virtual Constructors and Destructors • Destructors must be defined virtual. Why? • Consider base class A and a derived class B A* APtr ; //only the base class destructor APtr = new B; //is called with respect to the …… //object before the entire memory delete APtr; //occupied by the object is returned. This leads to memory leak apart from other problems, since destructor of B not called. • If destructor of class A is virtual, then first the destructor of class B will be called , then the destructor of class A will be called. Finally, the entire memory block occupied by the object will be returned to OS
If we expect the use of the delete operator on objects of a derived class in the presence of pointers of the base class, we must declare the destructor of the base class as virtual. • When a pointer of the base class points at a dynamically created object of the derived class and then deletes the memory occupied by the object, the entire block of memory is deleted. This is irrespective of whether the base class destructor is virtual or not.
Virtual constructors • Declaring a constructor virtual results in compile-time error. Why ? • If the constructor of class A is virtual, then in A* p = new B ; the constructor of class B alone will be called and that of class A will not be called. A rough pointer will result. • The need to construct virtually arises very frequently while programming in C++ void abc (A * p ) { // defn of abc() function } • Here an exact copy of the object at which ‘p’ points is required within the abc() function • Calling a copy constructor seems to serve the purpose i.e. A * q = new A (* p) or A A1(*p)
This will work if a correct copy constructor is defined and if ‘p’ points to an object of base class and not to the derived class object • If ‘p’ points to the derived class object, then the object created will not be of the same type and will be smaller in size with less data members since it creates the object of class A • A clone function is to be defined to solve this class A { public : virtual A * clone(){ return new A(* this);} //defn of class A }; class B : public A { public : virtual B * clone(){ return new B(* this);} //defn of class B };
Whenever a clone object is required, then the clone function is called and object created is subsequently destroyed void abc( A * p) { …. ; A * q = p -> clone(); …. ; delete q; } • Since clone function is virtual, its correct version is called. • Since the clone function constructs an object and is virtual, it is called a virtual constructor • There is actually nothing like virtual constructor
# include<iostream.h> class convert { protected : double val1; double val2; public : convert (double i) { val1 = i ; } double get conv() { return val2; } double getinit () { return val1 ; } virtual void compute = 0 ; }; class l_to_g : public convert { public : l_to_g (double i) : convert (i) { } void compute() { val2=val1/ 3.7854;} }; class f_to_c : public convert { public : f_to_c (double i) : convert (i) { } void compute() { val2=(val1-32)/ 3.7854;} };
int main() { convert * p; l_to_g lgob(4); f_to_c fcob(70); p= &lgob; cout<< p->getinit()<<“litres is :” ; p-> compute(); cout << p->getconv() << “gallons\n” ;} p= &fcob; cout<< p->getinit()<<“in fahrenheit is :” ; p-> compute(); cout << p->getconv() << “celcius\n” ; return 0; }
//feet to metres class f_to_m : public convert { public : f_to_m (double i) : convert (i) { } void compute() { val2=val1/ 3.28;} }; p= &fmob; // in main() cout<< p->getinit()<<“feet is :” ; p-> compute(); cout << p->getconv() << “metres\n” ;} • The class convert is an example of an abstract class. Virtual function compute is not defined with oin compute because no meaningful definition can be provided. The class convert simply does not contain sufficient information for compute to be defined. It is only when convert is inherited by a derived class that is a complete type is created.