Polymorphism Virtual Functions It was discussed earlier that

  • Slides: 30
Download presentation
Polymorphism, Virtual Functions • It was discussed earlier that polymorphism is the property of

Polymorphism, Virtual Functions • It was discussed earlier that polymorphism is the property of giving different meanings to the same thing. • The overloading of functions and operators have been described in details, in the previous slides. • This type of polymorphism is known as compile time or static polymorphism.

Compile time polymorphism or static binding: • To understand the compile time polymorphism, come

Compile time polymorphism or static binding: • To understand the compile time polymorphism, come to the concept of the function overloading, again. • Function overloading is the process of using the same function name for more than one operation. • The requirement in creating more than one function with the same name is that the compiler must be able to determine which function to use, on the basis of the number of arguments and the types of these arguments. • Since the compiler selects the appropriate function for a particular call at the compile time, hence this type of polymorphism is known as the compile time polymorphism, or early binding, or static binding.

Cont. • Now, consider a case when both the base class and the derived

Cont. • Now, consider a case when both the base class and the derived class have the same function name, with the same number and type of arguments. • In such situations, the compiler is unable to detect that what function should be invoked (member function of the base class or member function of the derived class). • For example, consider the following program: having a base class B and a derived class D, inherited from the base class B. Each of these classes have the same member function Display( ).

#include<iostream. h> class B // Base Class { public: void Display(void) { cout <<

#include<iostream. h> class B // Base Class { public: void Display(void) { cout << "n The member function of the base class B is invoked " ; } } ; class D 1 : public B // First derived class { public: void Display(void) { cout << "n The member function of the derived class D 1 is invoked " ; } }; class D 2 : public B // Second derived class { public: void Display(void) { cout << "n The member function of the derived class D 2 is invoked " ; } };

Cont. void main(void) { B* base_ptr ; // pointer to the object of the

Cont. void main(void) { B* base_ptr ; // pointer to the object of the base class B D 1 der 1_obj ; // object of the first derived class D 1 base_ptr = &der 1_obj ; base_ptr->Display( ) ; // Accessing the member function Display( ) D 2 der 2_obj ; // object of the second derived class D 2 base_ptr = &der 2_obj ; base->ptr->Display( ) ; // Accessing the member function Display( ) } ØDoesn’t the compiler complain that we are assigning an address of one type D 1 to a pointer of another B. ØThe compiler is perfectly happy, because type checking has been relaxed in this situation. ØThe rule is that pointers to objects of a derived class are type-compatible with pointers to object of the base class.

Output: As you can see, the function in the base class is always executed.

Output: As you can see, the function in the base class is always executed. The compiler ignores the contents of the pointer base_ptr and chooses the member function that matches the type of the pointer.

Description of the statements in the main( ) function Stmt. number Statement Description 1.

Description of the statements in the main( ) function Stmt. number Statement Description 1. B* base_ptr ; Creates a pointer base_ptr to the class B. 2. D 1 der 1_obj ; This statement creates an object der 1_obj of type D 1. 3. base_ptr = &der 1_obj ; Assigns the address of the object der 1_obj to the base_ptr. (Pointers to objects of a derived class are type-compatible with pointers to objects of the base class). 4. base_ptr->Display( ); Now, a question arises that after the execution of this statement, which function will be called? B : : Display( ) or D 1 : : Display( ). On watching the output of this program, it becomes confirmed that the function of the base class B is executed. 5. D 2 der 2_obj ; This statement creates an object der 2_obj of type D 2. 6. base_ptr = &der 2_obj ; Assigns the address of the object der 2_obj to the base_ptr. (Pointers to objects of a derived class are type-compatible with pointers to objects of the base class). 7. base_ptr->Display( ); Now, a question arises that after the execution of this statement, which function will be called? B : : Display( ) or D 2 : : Display( ). On watching the output of this program, it becomes confirmed that the function of the base class B is executed.

Cont. • Thus, the pointer selects the member function that matches the type of

Cont. • Thus, the pointer selects the member function that matches the type of the pointer, instead of the contents of the pointer. • This is an example of static binding in which the selection of the function call is made at the compile time.

Run-time polymorphism: • In place of static binding, one would like a binding method

Run-time polymorphism: • In place of static binding, one would like a binding method that is capable of determining which function should be invoked at run-time, on the basis of object type making call. • This type of binding is knows as late or dynamic binding, since the selection of the appropriate function is done dynamically at run-time. • To achieve dynamic binding, C++ offers a special feature known as virtual function. • A virtual function specification tells the compiler to create a pointer to a function but not to fill in the value of the pointer until the function is actually called. • Then at run-time and on the basis of the object making the call, the appropriate function address is used.

Cont. • When a function with same name, same number of arguments and same

Cont. • When a function with same name, same number of arguments and same type of arguments is used in both the base class and derived classes, the function in the base class can be made virtual by using a keyword virtual, preceding the function declaration. • For example, in the previous program, the function Display() in the base class B can be made virtual, by using the declaration as: virtual void Display(void) ; • When a function is made virtual, C++ determines which function to use at run-time based on the type of the object pointed to by the base class pointer, rather than the type of the pointer.

Now, consider the following program, which is same as that of the previous program,

Now, consider the following program, which is same as that of the previous program, but the member function Display( ) in the base class B is made virtual. // This program illustrates the use of the virtual function #include<iostream. h> class B // Base class { public: virtual void Display(void) // virtual function { cout << "n The member function Display( ) " ; cout << "of the "Base Class B" is invoked n" ; } }; class D 1 : public B // First derived class { public: void Display(void) { cout << "n The member function Display( ) " ; cout << "of the "Derived Class D 1" is invoked n" ; } };

Cont. class D 2 : public B // Second derived class { public: void

Cont. class D 2 : public B // Second derived class { public: void Display(void) { cout << "n The member function Display() " ; cout << "of the "Derived Class D 2" is invoked " ; } };

Cont. void main(void) { B* base_ptr ; // Pointer to the object of the

Cont. void main(void) { B* base_ptr ; // Pointer to the object of the base class D 1 der 1_obj ; // Object of the first derived class D 1 base_ptr = &der 1_obj ; /* The address of the object der 1_obj of the first derived class D 1 is assigned to the pointer base_ptr of the base class B */ base_ptr->Display( ); // Accessing the member function Display( ) D 2 der 2_obj ; // Object of the second derived class D 2 base_ptr = &der 2_obj ; /* The address of the object der 2_obj of the second derived class D 2 is assigned to the pointer base_ptr of the base class B */ base_ptr->Display( ); } // Accessing the member function Display( )

Output:

Output:

Abstract base class & pure virtual function: • The class that is not used

Abstract base class & pure virtual function: • The class that is not used to create objects is called an abstract class. • The only purpose of an abstract class is to act as a base class. • An abstract class is required when the base class is unable to create a meaningful implementation for a member function. • For example, in the following figure, the class Shape represents the abstract concept for which objects cannot exist. • A Shape makes sense only as the base of some class derived from it. • This can be seen from the fact that it is not possible to provide sensible definitions for its virtual functions. • A better alternative is to declare the virtual function of the class Shape to be pure virtual functions. • A virtual function is made pure by the initializer, = 0. Thus, the virtual functions in the abstract base class can be made pure as: virtual Enter_data( ) = 0 ; virtual Area( ) = 0; • A class with one or more pure virtual function is an abstract, and no object of that abstract class can be created. An abstract class can be used only as an interface and as a base for other classes.

Shape draw. Shape(); Rectangle draw. Shape(); Circle draw. Shape();

Shape draw. Shape(); Rectangle draw. Shape(); Circle draw. Shape();

Program: // This program illustrate the use of the pure virtual function #include<iostream. h>

Program: // This program illustrate the use of the pure virtual function #include<iostream. h> class Shape // Abstract Base Class { public: virtual void Enter_data( ) = 0; // pure virtual function virtual void Area( ) = 0 ; // pure virtual function }; class Rectangle : public Shape // First Derived class { private: float length ; float breadth ; public: void Enter_data(void) { cout << "n Enter the data for the Rectangle. . . . " ; cout << "nt Enter the length of the rectangle: " ; cin >> length ; cout << "t Enter the breadth of the rectangle: " ; cin >> breadth ; } void Area(void) { cout << "nt The area of the rectangle = " << (length * breadth) ; } };

Cont. class Circle : public Shape // Second Derived class { private: float radius

Cont. class Circle : public Shape // Second Derived class { private: float radius ; public: void Enter_data(void) { cout << "nn Enter the data for the Circle. . . " ; cout << "nt Enter the radius of the circle: " ; cin >> radius ; } void Area(void) { cout << "nt The area of the circle = " << (3. 14 * radius) ; } }; void main(void) { Shape* shp ; // pointer to the object of the base class Shape Rectangle rec ; // object of class Rectangle shp = &rec ; shp->Enter_data( ) ; shp->Area( ); Circle cir ; // object of class Circle shp = &cir ; shp->Enter_data( ); shp->Area( ); }

Output:

Output:

Virtual base classes: B D 1 D 2 Virtual path D 3

Virtual base classes: B D 1 D 2 Virtual path D 3

Cont. • In above Figure , the classes D 1 and D 2 are

Cont. • In above Figure , the classes D 1 and D 2 are derived from the base class B (hierarchical inheritance). • Also a class D 3 inherits the base class B (multilevel inheritance) through the classes D 1 and D 2 (multiple inheritance). • Each of the classes D 1 and D 2 inherit a copy of base class B known as suboject. • Now, come to the class D 3. When the derived class D 3 inherits the members of the base class B, which of two copies will it access (copy of sub-object of the class D 1 or D 2)? • This introduces an ambiguous situation for the compiler, hence it gives an error message. • For example, consider the following program, based on above design.

#include<iostream. h> class B // Base class { protected: int base_data ; public: void

#include<iostream. h> class B // Base class { protected: int base_data ; public: void get_b_data(int b) { cout << "n Accessing the data from the base class B------>" ; base_data = b ; } void display_b_data(void) { cout << "n base_data = " << base_data ; } }; class D 1 : public B // First base class. { // This class is derived from the class B. protected: int der 1_data ; public: void get_d 1_data(int d 1) { cout << "n Accessing the data from the derived class D 1 ----->" ; der 1_data = d 1 ; } void display_d 1_data(void) { cout << "n der 1_data = " << der 1_data ; } };

Cont. class D 2 : public B // Second virtual base class. { //

Cont. class D 2 : public B // Second virtual base class. { // This class is also derived from the class B protected: int der 2_data ; public: void get_d 2_data(int d 2) { cout << "n Accessing the data from the derived class D 2 ----->" ; der 2_data = d 2 ; } void display_d 2_data(void) { cout << "n der 2_data = " << der 2_data ; } };

Cont. class D 3 : public D 1, public D 2 // This class

Cont. class D 3 : public D 1, public D 2 // This class inherits the properties of the { // classes B, D 1 and D 2. public: int der 3_data ; public: void get_d 3_data(int d 3) { cout << "n Accessing the data from the derived class D 3 ----->" ; der 3_data = d 3 ; } void display_d 3_data(void) { cout << "n der 3_data = " << der 3_data ; } }; void main(void) { D 3 der 3 ; // Object of the class D 3. der 3. get_b_data(7); // Accessing the member functions of the base class B. der 3. display_b_data( ) ; der 3. get_d 1_data(8) ; // Accessing the member functions of the class D 1. der 3. display_d 1_data( ); der 3. get_d 2_data(18) ; // Accessing the member functions of the class D 2. der 3. display_d 2_data( ); der 3. get_d 3_data(15); // Accessing the member functions from its own class D 3. der 3. display_d 3_data( ); }

Output:

Output:

Cont. • To avoid such problem, the common derived classes D 1 and D

Cont. • To avoid such problem, the common derived classes D 1 and D 2 can be made virtual by using a keyword virtual. • This keyword is used with the declaration of the common base classes as: class D 1 : virtual public B or class D 1 : public virtual B // Declarative part class D 2 : virtual public B or class D 2 : public virtual B

Example: #include<iostream. h> // This program illustrates the use of the virtual base class

Example: #include<iostream. h> // This program illustrates the use of the virtual base class B // Base class { protected: int base_data ; public: void get_b_data(int b) { cout << "n Accessing the data from the base class B------>" ; base_data = b ; } void display_b_data(void) { cout << "n base_data = " << base_data ; } }; class D 1 : virtual public B // First virtual base class. { // This class is derived from the class B. protected: int der 1_data ; public: void get_d 1_data(int d 1) { cout << "n Accessing the data from the derived class D 1 ----->" ; der 1_data = d 1 ; } void display_d 1_data(void) { cout << "n der 1_data = " << der 1_data ; } };

Cont. class D 2 : virtual public B // Second virtual base class. {

Cont. class D 2 : virtual public B // Second virtual base class. { // This class is also derived from the class B. protected: int der 2_data ; public: void get_d 2_data(int d 2) { cout << "n Accessing the data from the derived class D 2 ----->" ; der 2_data = d 2 ; } void display_d 2_data(void) { cout << "n der 2_data = " << der 2_data ; } }; class D 3 : public D 1, public D 2 // This class inherits the properties of the { // classes B, D 1 and D 2. public: int der 3_data ; public: void get_d 3_data(int d 3) { cout << "n Accessing the data from the derived class D 3 ----->" ; der 3_data = d 3 ; } void display_d 3_data(void) { cout << "n der 3_data = " << der 3_data ; } };

Cont. void main(void) { D 3 der 3 ; // Object of the class

Cont. void main(void) { D 3 der 3 ; // Object of the class D 3. der 3. get_b_data(7); // Accessing the member functions of the base class B. der 3. display_b_data( ) ; der 3. get_d 1_data(8) ; // Accessing the member functions of the class D 1. der 3. display_d 1_data( ); der 3. get_d 2_data(18) ; // Accessing the member functions of the class D 2. der 3. display_d 2_data( ); der 3. get_d 3_data(15); // Accessing the member functions from its own class D 3. der 3. display_d 3_data( ); }

Output:

Output: