Object Oriented Programming
Using C++
By
Hemraj Saini
Associate Professor
Department of Computer Science and Engineering
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 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 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 << "\n The member function of the base class B is invoked " ; }
} ;
class D1 : public B // First derived class
{
public:
void Display(void)
{
cout << "\n The member function of the derived class D1 is invoked " ;
}
};
class D2 : public B // Second derived class
{
public:
void Display(void)
{
cout << "\n The member function of the derived class D2 is invoked " ;
}
};
Cont.
void main(void)
{
B* base_ptr ; // pointer to the object of the base class B
D1 der1_obj ; // object of the first derived class D1
base_ptr = &der1_obj ;
base_ptr->Display( ) ; // Accessing the member function Display( )
D2 der2_obj ; // object of the second derived class D2
base_ptr = &der2_obj ;
base_ptr->Display( ) ; // Accessing the member function Display( )
}
Doesn’t the compiler complain that we are assigning an address of one
type D1 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.
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. Statement Description
number
1. B* base_ptr ; Creates a pointer base_ptr to the class B.
2. D1 der1_obj ; This statement creates an object der1_obj of type D1.
3. base_ptr = &der1_obj ; Assigns the address of the object der1_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 D1
:: Display( ). On watching the output of this program, it
becomes confirmed that the function of the base class B is
executed.
5. D2 der2_obj ; This statement creates an object der2_obj of type D2.
6. base_ptr = &der2_obj ; Assigns the address of the object der2_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 D2
:: 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 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
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 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, 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 D1 : public B // First derived class
{
public:
void Display(void)
{
cout << "\n The member function Display( ) " ;
cout << "of the \"Derived Class D1\" is invoked \n" ;
}
};
Cont.
class D2 : public B // Second derived class
{
public:
void Display(void)
{
cout << "\n The member function Display() " ;
cout << "of the \"Derived Class D2\" is invoked " ;
}
};
Cont.
void main(void)
{
B* base_ptr ; // Pointer to the object of the base class
D1 der1_obj ; // Object of the first derived class D1
base_ptr = &der1_obj ; /* The address of the object der1_obj of the
first derived class D1 is assigned to the
pointer base_ptr of the base class B */
base_ptr->Display( ); // Accessing the member function Display( )
D2 der2_obj ; // Object of the second derived class D2
base_ptr = &der2_obj ; /* The address of the object der2_obj of the
second derived class D2 is assigned to the
pointer base_ptr of the base class B */
base_ptr->Display( ); // Accessing the member function Display( )
}
Output:
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 void Enter_data( ) = 0 ;
virtual void 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
drawShape();
Rectangle Circle
drawShape(); drawShape();
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 << "\n\t Enter the length of the rectangle: " ;
cin >> length ;
cout << "\t Enter the breadth of the rectangle: " ;
cin >> breadth ;
}
void Area(void)
{
cout << "\n\t The area of the rectangle = " << (length * breadth) ;
}
};
Cont.
class Circle : public Shape // Second Derived class
{
private:
float radius ;
public:
void Enter_data(void)
{
cout << "\n\n Enter the data for the Circle..........." ;
cout << "\n\t Enter the radius of the circle: " ;
cin >> radius ;
}
void Area(void)
{
cout << "\n\t The area of the circle = " << (3.14 * radius * 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:
Program:
class Card // An abstract class
{
protected:
char recipient[20];
public:
virtual void greeting()=0; // A pure virtual function
};
class Holiday : public Card
{
public:
Holiday( char r[ ] )
{
strcpy(recipient, r) ;
}
void greeting() // implementation of function in sub class
{
cout << "Dear " << recipient << endl ; cout
<< "Season's Greetings!\n\n“ ;
}
};
Cont.
class Birthday : public Card
{
private:
int age;
public:
Birthday ( char r[ ], int years )
{
strcpy(recipient, r);
age = years;
}
void greeting() // Implementation of function in sub class
{
cout << "Dear " << recipient << ",\n“ ;
cout << "Happy " << age << "th << “Birthday\n\n";
}
};
Cont.
class Holi : public Card
{
private:
int colors;
public:
Holi( char r[ ], int c )
{
strcpy(recipient, r) ;
colors = c;
}
void greeting() // Implementation of function in subclass
{
cout << "Dear " << recipient << ",\n";
cout << “ Happy holi and lots of colors for you\n";
for ( int j=0; j < colors; j++ )
cout << “*";
cout << "\n\n“ ;
}
};
Cont.
void main ( )
{
Holiday hol ( “abc” );
hol.greeting();
Birthday bd( “def”, 21 );
bd.greeting();
Holi holi( “xyz”, 7 );
holi.greeting();
}
Virtual base classes:
D1 D2
Virtual path
D3
Cont.
• In above Figure , the classes D1 and D2 are derived
from the base class B (hierarchical inheritance).
• Also a class D3 inherits the base class B (multilevel
inheritance) through the classes D1 and D2 (multiple
inheritance).
• Each of the classes D1 and D2 inherit a copy of base
class B known as suboject.
• Now, come to the class D3. When the derived class D3
inherits the members of the base class B, which of two
copies will it access (copy of sub-object of the class D1
or D2)?
• 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 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 D1 : public B // First base class.
{ // This class is derived from the class B.
protected:
int der1_data ;
public:
void get_d1_data(int d1)
{
cout << "\n Accessing the data from the derived class D1---------->" ;
der1_data = d1 ;
}
void display_d1_data(void)
{
cout << "\n der1_data = " << der1_data ;
}
};
Cont.
class D2 : public B // Second virtual base class.
{ // This class is also derived from the class B
protected:
int der2_data ;
public:
void get_d2_data(int d2)
{
cout << "\n Accessing the data from the derived class D2---------->"
;
der2_data = d2 ;
}
void display_d2_data(void)
{
cout << "\n der2_data = " << der2_data ;
}
};
Cont.
class D3 : public D1, public D2 // This class inherits the properties of the
{ // classes B, D1 and D2.
public:
int der3_data ;
public:
void get_d3_data(int d3)
{
cout << "\n Accessing the data from the derived class D3---------->" ;
der3_data = d3 ;
}
void display_d3_data(void)
{ cout << "\n der3_data = " << der3_data ; }
};
void main(void)
{
D3 der3 ; // Object of the class D3.
der3.get_b_data(7); // Accessing the member functions of the base class B.
der3.display_b_data( ) ;
der3.get_d1_data(8) ; // Accessing the member functions of the class D1.
der3.display_d1_data( );
der3.get_d2_data(18) ; // Accessing the member functions of the class D2.
der3.display_d2_data( );
der3.get_d3_data(15); // Accessing the member functions from its own class D3.
der3.display_d3_data( );
}
Output:
Cont.
• To avoid such problem, the common derived
classes D1 and D2 can be made virtual by using
a keyword virtual.
• This keyword is used with the declaration of the
common base classes as:
class D1 : virtual public B
or
class D1 : public virtual B // Declarative part
class D2 : virtual public B
or
class D2 : public virtual B
Example:
#include<iostream.h>
// This program illustrates the use of the virtual base clas
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 D1 : virtual public B // First virtual base class.
{ // This class is derived from the class B.
protected:
int der1_data ;
public:
void get_d1_data(int d1)
{
cout << "\n Accessing the data from the derived class D1---------->" ;
der1_data = d1 ;
}
void display_d1_data(void)
{
cout << "\n der1_data = " << der1_data ;
}
};
Cont.
class D2 : virtual public B // Second virtual base class.
{ // This class is also derived from the class B.
protected:
int der2_data ;
public:
void get_d2_data(int d2)
{
cout << "\n Accessing the data from the derived class D2---------->" ;
der2_data = d2 ;
}
void display_d2_data(void)
{
cout << "\n der2_data = " << der2_data ;
}
};
class D3 : public D1, public D2 // This class inherits the properties of the
{ // classes B, D1 and D2.
public:
int der3_data ;
public:
void get_d3_data(int d3)
{
cout << "\n Accessing the data from the derived class D3---------->" ;
der3_data = d3 ;
}
void display_d3_data(void)
{
cout << "\n der3_data = " << der3_data ;
}
};
Cont.
void main(void)
{
D3 der3 ; // Object of the class D3.
der3.get_b_data(7); // Accessing the member functions of the base class B.
der3.display_b_data( ) ;
der3.get_d1_data(8) ; // Accessing the member functions of the class D1.
der3.display_d1_data( );
der3.get_d2_data(18) ; // Accessing the member functions of the class D2.
der3.display_d2_data( );
der3.get_d3_data(15); // Accessing the member functions from its own class D3.
der3.display_d3_data( );
}
Output: