OOP_L31to34

advertisement
Chapter 7
Virtual Functions and Dynamic
Polymorphism
Objectives
•
•
•
•
•
The need for virtual functions
About virtual functions
The mechanism of virtual functions
Pure virtual functions and abstract base class
Virtual destructors and constructors
The Need for Virtual Functions
• Example 1 shows overriding member function of base class in the derived class.
Example 1:
class A
{
public: void show() { cout<<“show A\n” }
};
class B: public A
{
public: void show() { cout<<“show B\n” }
};
• Example 2 shows calling an overridden function through a pointer of base class
type:
Example 2:
void main()
{
A *Aptr;
A A1;
B B1;
Aptr=& A1;
Aptr->show();
Aptr=&B1;
Aptr->show();
The Need for Virtual Functions
• In the Example 2, the base class function is called irrespective of the type of
object pointed at by the pointer.
• The compiler decides which function is to be called by considering the type of
the pointer; the type of the object pointed at by the pointer is not
considered.
• Overriding in such cases is ineffective. This can be a serious problem when a
client is trying to extend a class hierarchy.
• Calling an overridden function through a reference of base class type
produces the same effect. (See Example 3).
Example 3
void main()
{
A A1;
B B1;
A &Aref1= A1;
A &Aref2 = B1;
}
Aref1.show();
Aref2.show();
The Need for Virtual Functions
• Placing the pointer and the object pointed at by the pointer in the same
function as local variables does not solve the issue since an object can
be as effectively accessed through its name itself.
• Instead, the pointer appears as a formal argument in function definitions
and the address of the object is passed as a parameter to the function
calls. Similar comments hold true for the reference variable also.
• Calling an overridden function through a pointer of base class type, where
the base class function will get successively refined by the overriding
functions of the derived classes, is also ineffective. (See Example 4).
The Need for Virtual Functions
Example 4
class X
{public:
void abc(A *);
}
void X::abc (A *p)
{
p->show();
}
void main()
{
X X1;
A A1;
B B1;
X1.abc(&A1);
X1.abc(&B1);
}
// A::show() will be called
// A::show() will be called
• Thus, it proves impossible to extend an existing class hierarchy. Function
overriding does not produce the desired effects. Virtual functions solve this problem.
Virtual Functions
• Virtual functions provide one of the most useful and powerful features of C++
called dynamic polymorphism. Consider the following example for
understanding this concept
. Suppose the library programmer has to write a code for computing sum
between the ranges a and b (factorial sum , cube sum, log sum etc.) and the
application programmer can select any of the sum function available in the
library, just by passing the function name as an argument to the function
generic Sum function:
Example 5:
int factSum(int a, int b)
{ int i;
long sum;
for (sum=0, i=a; i<=b;i++)
sum+=fact(i);
return sum;}
Virtual Functions
Example 6:
int CubeSum(int a, int b)
{ int i;
long sum;
for (sum=0, i=a; i<=b;i++)
sum+=cube(i);
return sum;}
Example 7:
int LogSum(int a, int b)
{ int i;
long sum;
for (sum=0, i=a; i<=b;i++)
sum+=log(i);
return sum;}
Virtual Functions
• From the 3 mentioned functions it is clear that the definitions are same except
the name of the inner function they all call.
• So it is possible to replace the definition of these functions by a single function.
The example 8 shows a generic function that will replace all 3 functions .
• For this we have to pass a function pointer as an additional formal argument:
Example 8:
long int GenericSum(int a, int b, long int (*p )(int i) )
{ int i;
long sum;
for (sum=0, i=a; i<=b;i++)
sum+=(*p) (i);
return sum;}
• Now, any of the sum function can be called by passing the function whose returned
values have to be summed up as the last parameter to this generic function.
• For example:
void main()
{
cout<<GenericSum(10,50,fact);
cout<<GenericSum(10,50,cube);
Virtual Functions
• The function call:
(*p) (i) ,
exhibits the polymorphic behaviour while compiling it, it is not known
which function will actually be executed.
• This becomes known only later when the client program that calls the
GenericSum() function.
• Similar effect must also happen for the class objects in C++.
• Depending
on
the
address
contained
in
the
common
pointer,
corresponding member functions of the different classes have to be
selected.
Virtual Functions
• If the library programmer, who is defining the base class, expects and
suspects overriding of a certain member function and wants to make such
an override meaningful, he/she should declare the function as virtual.
• For declaring a function as virtual, the prototype of the function within the
class should be prefixed with the virtual keyword.
• The virtual keyword may appear either before or after the keyword
specifying the return type of the function.
• If the function is defined outside the class, then only the prototype should
have the virtual keyword.
• The syntax for declaring a virtual function as follows:
virtual <return type> <function name>(<formal arguments>);
Virtual Functions
Example 9:
class A
{
public: virtual void show() { cout<<“show A\n” }
};
class B: public A
{
public: void show() { cout<<“show B\n” }
};
void main()
{
A *Aptr;
A A1;
B B1;
Aptr=& A1;
Aptr=&B1;
}
Aptr->show();
Aptr->show();
Virtual Functions
• 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, and not the overridden function of the base
class, that is called.
• The function call exhibits polymorphic behaviour, because while compiling it,
it is not known which function will actually be executed.
• This is decided only when the client program that calls this function is
compiled. We can therefore say that compile time for the client is run time for
the library. Therefore, the polymorphic
behaviour exhibited by virtual
functions is also termed as dynamic polymorphism.
• The functions in the base class usually contain only those statements that
are relevant to the base class itself.
Virtual Functions
• The overriding functions of the derived class first call back the overridden
base class functions and then add the extra statements that complete the
definitions with respect to the derived class itself.
• Having such base class functions as virtual ensures that the client is able
to call both the functions in sequence as desired.
• Virtual functions of the base classes reappear as virtual in the
derived classes also.
Virtual Functions
Example 10:
class A
{
public: virtual void show() { cout<<“show A\n” }
};
class B: public A
{
public: void show() { cout<<“show B\n” }
};
class C: public B
{
public: void show() { cout<<“show C\n” }
};
void main()
{
B*Bptr;
C C1;
Bptr=&C1; Bptr->show();
The Mechanism of Virtual Functions
•
For every base class that has one or more virtual functions, a table of function
addresses is created during run time. This table of function addresses is called
the virtual table or VTBL in short.
• Such a table of addresses of virtual functions will be created for the derived class as well.
• The VTBL contains the address of each and every virtual function that has been
defined in the corresponding class.
Example 11:
class A
{ public:
virtual void abc();
virtual void def();
};
• During runtime, the VBTL of class A will be:
Address of A::abc()
101
201
Address of A::def()
The Mechanism of Virtual Functions
• Addresses of non-virtual functions do not appear in such tables.
• A table of addresses of virtual functions will be created for the derived class also.
• If the derived class does not redefine a certain base class member, then the table
will contain the address of the inherited base class virtual function itself.
• However, if a certain base class virtual function is redefined in the derived class,
this table will contain the address of the overriding function.
• Also, if the derived class defines a new virtual function, then its address will also
be contained in the table. (See Example 12 and Figure).
Example 12:
class B: public A
{ public:
void def();
void virtual ghi();
Address of B::def()
};
The VBTL for class B appears as:
101
301
Address of A::abc()
401
Address of B::ghi()
The Mechanism of Virtual Functions
• Every object of a class that has a virtual function contains a pointer to the VTBL of
the corresponding class. This pointer is also known as the virtual pointer or VPTR.
• Whenever a call is dispatched to a virtual function through an object or a reference
to an object, or through a pointer to an object, then first of all the value of the
VPTR of the object is read.
• Then the address of the called function from the corresponding VTBL is obtained.
Finally, the function is called through the address thus obtained.
• The virtual table size varies from class to class (for each class there is only one
VTBL).
•
The size of the object does not vary. Only the size of the objects of classes with
virtual functions increases uniformly by four bytes due to the presence of the
additional pointer (VPTR).
The Mechanism of Virtual Functions
Why not declare functions virtual by default? Why does C++ support
not support dynamic binding only?
•
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.
• If none of the member functions of a certain class will be
overridden, then making them virtual will unnecessarily incur the
above cost.
Virtual Functions




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.
If the library programmer, who is defining the base
class, expects and suspects overriding of a certain
member function and wants to make such an override
meaningful, he/she should declare the function as
virtual.
For declaring a function as virtual, the prototype of the
function within the class should be prefixed with the
virtual keyword.
Pure Virtual Functions
• There are cases where the library programmer would like to enforce an
override of the base class 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 <return_type> <function_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.
Pure Virtual Functions
When should pure virtual functions be used in C++?
In C++, a regular, "non-pure" virtual function provides a definition, which means that the class in
which that virtual function is defined does not need to be declared abstract. You would want to
create a pure virtual function when it doesn’t make sense to provide a definition for a virtual
function in the base class itself, within the context of inheritance.
An example of when pure virtual functions are necessary
For example, let’s say that you have a base class called Figure. The Figure class has a function
called draw. And, other classes like Circle and Square derive from the Figure class. In the Figure
class, it doesn’t make sense to actually provide a definition for the draw function, because of
the simple and obvious fact that a “Figure” has no specific shape. It is simply meant to act as a
base class. Of course, in the Circle and Square classes it would be obvious what should happen
in the draw function – they should just draw out either a Circle or Square (respectively) on the
page. But, in the Figure class it makes no sense to provide a definition for the draw function.
And this is exactly when a pure virtual function should be used – the draw function in the Figure
class should be a pure virtual function.
Pure Virtual Functions
• A virtual function that is initialized to zero (0) is referred to as pure virtual function.
It has no body and hence also known as do-nothing or the dummy function.
Example: virtual void show()=0;
•
A class containing one or more pure virtual functions is called an Abstract Base
Class (ABC), which means an instance of such class can't be created (but pointer
to that class can be created).
• We should use pure virtual function if we do not want to instantiate a class but
make it act as a base class for all the classes that derive from it.
Pure Virtual Functions
Sample program:
class alpha
{
public:virtual void show()=0; //pure virtual function
};
class beta:public alpha
{
public:void show() //overriding
{
cout<<"OOP in C++";
}
};
void main()
{
alpha *p;
beta b;
p=&b;
p->show();
}
Pure Virtual Functions
An important thing to note about pure virtual functions is that these functions must
be overridden in all the derived classes otherwise the compile would flag out an
error.
Sample program:
class A {
public:
void virtual abc()=0;
void virtual def()=0;
void virtual ghi()=0;
};
void main()
{
A A1; //error
}
Pure Virtual Functions
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
}
Pure Virtual Functions
• Pure virtual functions provide a mechanism to the library programmer to enforce
the desired override.
• If even one member function is declared as a pure virtual function, then the
corresponding class becomes an Abstract Base Class (ABC in short).
• A function is declared as a pure virtual function by prefixing its prototype with the
virtual keyword as before but suffixing it with an ‘equal to’ sign and then by a ‘zero’
(0).
•
The syntax for declaring a pure virtual function is:
virtual <return type> <function name>(<formal arguments>)=0;
• An abstract base class cannot be instantiated, that is, objects of an abstract base
class cannot be declared. Compile-time errors defeat attempts to do so.
Pure Virtual Functions
• The derived class must override all pure virtual functions of the base class or itself
get branded as an ABC by the compiler.
• The library programmer defines the ABC and also some generic functions that
implement the general flow of a related algorithm without considering the exact
data type on which they will work. Thus, the utility of an ABC lies in its use as an
interface.
Pure Virtual Functions
Advantages of ABC
• The ABC behaves just like an interface with little or no implementation of its own.
• The library programmer is free to define generic functions without bothering about
the implementation details.
• The library programmer can enforce all necessary overrides.
• The application programmer can derive any class from the ABC, provide his/her
own implementations for the derived class and then use the same driver functions
for any of these derived classes.
•
Abstract Base Classes are also used to build implementation in stages. We know
that 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.
Pure Virtual Functions
•
The ability to provide an implementation to pure virtual methods allows data types to
provide core functionality while still requiring derived classes to provide a specialized
implementation.
•
The class remains abstract even if we provide an implementation for its pure virtual
function.
•
A member function can be non-virtual, virtual, or pure virtual.
Pure Virtual Functions
Example: Class Shape accepts two values (Data Type: Double). Create two derived
classes Triangle and Rectangle ; calculate area of Triangle and rectangle using data
members from base class. Use one virtual function to display data.
class Shape
{
protected :
double x0, y0;
public :
void getdata(double x, double y)
{
x0 = x;
y0 = y;
}
virtual void displayArea() = 0;
};
Pure Virtual Functions
class Triangle : public Shape
{
public :
void getdata(double x, double y)
{
Shape :: getdata(x,y); }
void displayArea()
{
double area = (x0 * y0)/2.0;
cout << "\n Area of triangle = " << area; }
};
class Rectangle : public Shape
{
public :
void getdata(double x, double y)
{
Shape :: getdata(x,y);
}
void displayArea()
{
double area = (x0 * y0);
cout << "\n Area of rectangle = " << area;
}
};
Pure Virtual Functions
void main()
{
Triangle *tptr;
Rectangle *rptr;
Shape *sptr[2];
int i;
tptr = new Triangle;
rptr = new Rectangle;
tptr->getdata(20.0,30.0);
rptr->getdata(20.0,20.0);
sptr[0] = tptr;
sptr[1] = rptr;
for(i=0; i<2; i++)
sptr[i]->displayArea();
getch();
}
Question: How to derive another class circle from the base class and call displayarea ()
function?
Virtual Destructors
• Destructors can be defined as virtual.
• The destructor of a base class must be declared as virtual if the delete
operator is to be used on objects of a base class and there is a presence
of pointers in the derived classes.
• 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 Destructors
Destructors must be defined virtual. Why?
For e.g.. Consider base class A and a derived class B
class A
{
public:
~A() ;
};
};
class B: public A
{
public:
~B() ;
};
};
void main()
{ 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.
Virtual Destructors
• This leads to memory leak apart from other problems, since destructor of
B is 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.
Virtual Constructors
• Constructors cannot be virtual. Declaring a constructor as virtual results in a
compile-time error.
• There are valid reasons that justify the above statement:
 To create an object, the constructor of the object class must be of the same type
as the class. But, this is not possible with a virtually implemented constructor.
 At the time of calling a constructor, the virtual table would not have been created
to resolve any virtual function calls. Thus, a virtual constructor would not have
been anywhere to look up.
Virtual Constructors
• However, the need to construct virtually arises very frequently while
programming in C++.
• Calling the copy constructor seems to serve the purpose . For this, a clone
function will have to be defined.
• Derived classes will similarly define and override this clone function.
• Whenever a clone of the object is required, the clone function is called. The clone
object created is subsequently destroyed.
• Since the clone function constructs an object and is also virtual, it is sometimes
called a virtual constructor. However, note that there is actually nothing like a
virtual constructor.
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
Here is a simple example:
Virtual constructor
class AbstractBase
{
public:
virtual ~AbstractBase() {}
virtual AbstractBase * Create() = 0;
virtual AbstractBase * Clone() = 0;
// ...
};
class SomeType : public AbstractBase
{
public:
virtual SomeType * Create();
virtual SomeType * Clone();
// ...
};
Virtual constructor
SomeType * SomeType::Create()
{
return new SomeType;
}
SomeType * SomeType::Clone()
{
return new SomeType(*this);
}
int main()
{
AbstractBase *p;
Derived d;
p = &d;
}
Questions
1. Create a class called Shipment which consists of data members
total_weight (float type) and no_of_packages (int type). Write a
parameterized constructor. Also write a member function for
overloading the minus sign. This function should
subtract
total_weight of second object from total_weight of first object and
return this value. Write a suitable main() function.
2. Create a class called Distance consisting of data members ifeet (int
type) and finches (float type). Write functions for overloading the
extraction operator and the insertion operator for objects of this
class. Write a complete program using these functions.
Questions
3. Given a program fragment as follows –
class Matrix {
int mat[10][10];
int row, col;
public:
Matrix(int m[10][10], int r, int c); //parameterized constructor
};
void main( ){
int m1[10][10] = {{1,1,1},{2,2,2},{3,3,3}};
Matrix A(m1,3,3);
cout << A; //display contents of matrix – row by row
++A; //increment each element of the matrix by 1
cout << A;
Questions
A. Write a member function (including prototype) for overloading the
increment operator. This function increments each element of the
matrix by 1.
B. Write a function (incuding prototype) for overloading the insertion
operator (<<). This function should display the contents of the matrix
row by row, that is, one row per line.
4. Write a program to create a graphic class hierarchy. Create an
abstract base class called Figure and derive two classes Square (side)
and Circle (radius). Derive classes SquarePrism (height) from Square
and Cylinder (height) from Circle. Include the appropriate
constructors and the member function Show() to display the details of
the object. Create the objects of the different classes. Demonstrate
the dynamic polymorphism through invoking the Show() function for
the objects created. (Note – Information given with class names
within parentheses are data members).
Download