Loading....
Coupon Accepted Successfully!

 

Working Of Virtual Functions

Before learning about the mechanism of virtual functions, let us revise a few points related to virtual functions:
  1. Binding means a link between a function call and the real function that is executed when the function is called.
  2. When a compiler knows which function to call before execution, it is known as early binding.
  3. Dynamic binding means the actual function invoked at run time is dependent on the address stored in the pointer. In this binding, a link between function call and actual function is made during program execution.
  4. The keyword virtual prevents the compiler from performing early binding. Binding is postponed until program execution.
The following programs illustrate the step-by-step working of virtual functions:
 

15.10 Write a program to define virtual and non-virtual functions and determine the size of the objects.

#include<iostream.h>

#include<conio.h>

class A

{

private:

int j;

public:

virtual void show() {cout<<endl<<“In A class”;}

};

class B

{

private:

int j;

public:

void show() {cout<<endl<<“in B class”;}

};

class C

{

public:

void show() {cout<<endl<<“In C class”;}

};

void main()

{

clrscr();

A x;

B y;

C z;

cout<<endl<<“Size of x=”<<sizeof (x);

cout<<endl<<“Size of y=”<<sizeof (y);

cout<<endl<<“Size of z=”<<sizeof (z);

}

OUTPUT
Size of x = 4
Size of y = 2
Size of z = 1

Explanation:
In the above program, the class A has only one data element of integer type, but the size of the object displayed is 4. The size of the object of class B that contains a one-integer data element is two, and finally, the size of the object of class C is displayed as one, even if the class C has no data element.

The function show() of class A is prefixed by the virtual keyword. Without the virtual keyword, the size of objects would be 2,2 and 1. The size of object x with a virtual function in class A is the addition of data member int (2 bytes) and void pointers (2 bytes). When a function is declared as virtual, the compiler inserts a void pointer. In class C, even if it has no object, the size of the object displayed is 1, and this is due to the compiler, who assumes the size of object z not to be zero, as every object should have an individual address. Hence, minimum size one is considered. The minimum nonzero positive integer is one.
 
To perform late binding, the compiler establishes VTABLE (virtual table) for every class and its derived classes having a virtual function. The VTABLE contains addresses of the virtual functions. The compiler puts the address of the virtual functions in the VTABLE. If no function is redefined in the derived class that is defined as virtual in the base class, the compiler takes the address of the base class function.
 
When an object of the base or derived class is created, a void pointer is inserted in the VTABLE, called VPTR (vpointer). The VPTR points to the VTABLE. When a virtual function is invoked, using the base class pointer, the compiler speedily puts a code to obtain the VPTR and searches for the address of the function in the VTABLE. In this way, an appropriate function is invoked, and dynamic binding takes place.
 
The VPTR should be initialized with the beginning address of the apposite VTABLE. When the VPTR is initialized with the apposite VTABLE, the type of the object can be determined by itself. However, it is useless if is applied at the point when a virtual function is invoked.
 
Assume a base class pointer object points to the object of a derived class. If a function is invoked using the base class pointer, the compiler uses a different code to accomplish the function call. The compiler begins from the base class pointer. The base class pointer holds the address of the derived class object. With the aid of this address, the VPTR of the derived class is obtained. Via VPTR, the VTABLE of the derived class is obtained. In the VTABLE, the address of the function being invoked is acquired and the function is called. All the above processes are handled by the compiler, and the user need not worry about them. The following program makes the concept clearer:
 

15.11 Write a program to define virtual member functions in derived classes.

#include<iostream.h>

#include<conio.h>

class shop

{

public:

virtual void area() {cout<<endl<<“In area of shop”;}

virtual void rent() {cout<<endl<<“In rent of shop”;}
void period() {cout<<endl<<“In period of shop”;}

};
class shopA : public shop
{

public:
void area() {cout<<endl<<“In area of shopA”;}
void rent() {cout<<endl<<“In rent of shopA”;}

};
class shopB : public shop
{

public:
void area() {cout<<endl<<“In area of shopB”;}
void rent() {cout<<endl<<“In rent of shopB”;}
void period() {cout<<endl<<“In period of shopB”;}

};
class shopC: public shop
{

void area() {cout<<endl<<“In area of shopC”;}

};
void main()
{

clrscr();
shop *p;
shop s;
p=&s;
p->area();
p->rent();
p->period();
shop *sa,*sb,*sc;
shopA a;
shopB b;
shopC c;
sa=&a;
sb=&b;
sc=&c;
sa->area();
sa->rent();
sb->area();
sb->rent();
sc->area();
sc->rent();
sa->period();

e.area();
sb->period();
shop d;
d.area();
shopA e;
shopC f;
f.rent();

}

OUTPUT
In area of shop
In rent of shop
In period of shop
In area of shopA
In rent of shopA
In area of shopB
In rent of shopB
In area of shopC
In rent of shop
In period of shop
In period of shop
In area of shop
In area of shopA
In rent of shop


Explanation:
In the above program, four classes are declared, as shown in Figure.
 


Fig: 
Base and derived classes
 


Fig: PTR AND VTABLES

 

As discussed earlier, a VTABLE is formed for each class having a virtual function and for the derived class of the same class. The VTABLE is formed for the following classes: shop, shopA, shopB, and shopC. All of the above four VTABLES hold the address of virtual functions. In addition, the compiler would place a VPTR that points to the particular VTABLE, as shown in Figure.
 
The class shopC is without functions rent(). Therefore, the VTABLE holds the address of the base class rent() function. Consider the following statements:
shop *p; // Base class object pointer
shop s; // object of base class
p = &s; // Address of s is assigned to p
 
The pointer p contains the address of the object s. Now, consider the following statements:
p->area(); // Invokes function area() of base class
p->rent(); // Invokes function rent() of base class
p->period(); // Invokes function period() of base class
 
From the above functions, first two function area() and rent() are virtual, and period() is a non-virtual function. Though the address of the base class object or derived class object is stored in the base class pointer, the function of the base class is invoked, because the period() is a non-virtual function. The member function area() is declared as virtual in the base class. All the three derived classes shopA, shopB, and shopC contain the function area(). The execution of these functions depends on the address stored in the base class pointer. For example, p contains the address of object a; the functions of class shopA are invoked. Similarly, for storing addresses of objects b and c, functions of classB and classC can be invoked.
p = &s; // Address of s is assigned to p
 
In the above statement, p contains the address of the object s (base class). Therefore, when the function area() is invoked by the pointer p, VPTR is created from the object s. With the help of VPTR, VTABLE of the class shop is obtained, and the address of the function area() for class shop is accessed. With the aid of an address, shop::area() is finally invoked. Consider the following statements:
shop *sa; // object pointer declaration
shopA a; // object of derived class
sa = &a; // assigns address of derived class object to base class pointer
sa->area(); // invokes function area()
sa->rent(); // invokes function rent()
 
In the statement sa = &a; the address of the derived class object is stored in the base class pointer. When the function area() is invoked, the VPTR of object a is used to obtain the VTABLE of class shopA. From this VTABLE, the address of shopA :: area() and shopA :: rentare obtained and finally invoked. Likewise, for objects b and c, binding is achieved.
Consider the following statements:
sa->period();
sb->period();
 
Function period() is a non-virtual function. The VTABLE is not used to call the function shop :: period(). In addition, the function period() is not redefined in the derived classes. Now, concentrate on the following statements:
shop d; // Base class object
d.area();
shopA e; // Derived class object
e.area();
shopC f; // Derived class object
f..rent();
 
In the above statements, dynamic binding is not performed, and, hence, VPTR and VTABLE are not created. The functions are called as usual.
 
C++ places addresses of the virtual member function in the virtual table. When these member functions are called, the accurate address is obtained from the virtual table. This entire procedure takes time. Therefore, the virtual function makes program execution a bit slow. Conversely, it provides better flexibility.





Test Your Skills Now!
Take a Quiz now
Reviewer Name