|
 |
[Introduction] [Chapter 1] [Chapter
2] [Chapter 3] [Chapter
4] [Chapter 5] [Chapter
6] [Chapter 7] [Chapter
8] [Chapter 9] [ Chapter
10 ] [Chapter 11] [Chapter
12]
Chapter 10: Virtual Functions
Once again we are into a completely new topic with terminology which
will be new to you. If you are new to object oriented programming,
you should follow along in this chapter very carefully because every
attempt has been made to define every detail of this new and somewhat
intimidating topic. However, if you are well versed in object oriented
programming, and simply learning C++ as a new language, you may wish
to skip the first four programs in this chapter and go directly to
the example program named VIRTUAL5.CPP and continue from there to
the end of the chapter.
One term which must be defined is polymorphism, a rather large word
that simply means similar when used in the context of object oriented
programming. Objects are polymorphic if they have some similarities
but are still somewhat different. We will see how it is used in the
context of object oriented programming as we proceed through this
chapter.
We have already studied operator overloading and function overloading
in this tutorial, and they are a subtle form of polymorphism since
in both cases, a single entity is used to refer to two or more things.
The use of virtual functions can be a great aid in programming some
kinds of projects as you will see in these two chapters.
A SIMPLE PROGRAM WITH INHERITANCE
Example program > VIRTUAL1.CPP
Examine the example program named VIRTUAL1.CPP for the basic program
outline we will use for all discussion in this chapter. Since this
program has nothing to do with virtual functions, the name may be
somewhat misleading. It is named VIRTUAL1.CPP because it is part of
a series of programs intended to illustrate the use of virtual functions.
The last program in this chapter will illustrate the proper use of
virtual functions.
The first program is very simple and you will recognize it as being
similar to the programs studied earlier in this tutorial except that
this program is greatly simplified in order to effectively instruct
you in the use of a virtual function. You will notice that many of
the methods from the last chapter have been completely dropped from
this example for simplicity, and a new method has been added to the
parent class, the method named message() in line 9. Throughout this
chapter we will be studying the operation of the method named message()
in the base class and the derived classes. For that reason, there
is a method named message() in the car class as well as in the new
class named boat in lines 30 through 36.
You will also notice that there is a lack of a method named message()
in the truck class. This has been done on purpose to illustrate the
use of the virtual method, or if you prefer, you can refer to it as
a virtual function. You will recall that the method named message()
from the base class is available in the truck class because the method
from the base class is inherited with the keyword public included
in line 21.
The method named message() in the base class and in the derived classes
has been kept very simple on purpose. Once again, we are interested
in the technique of the virtual method rather than a long complicated
example.
The main program is as simple as the classes, one object of each of
the classes is defined in lines 41 through 44 and the method named
message() is called once for each object. The result of executing
the program indicates that the method for each is called except for
the object named semi, which has no method named message(). As discussed
in the last chapter, the method named message() from the parent class
is called and the data output to the monitor indicates that this did
happen since it displays "Vehicle message" for the object
named semi.
The data for the objects is of no concern in this chapter so all data
is allowed to default to private type and none is inherited into the
derived classes. Some of the data is left in the example program simply
to make the classes look like classes. Based on your experience with
C++ by now, you realize that the data could be removed since it is
not used.
After you understand this program, compile and execute it to see if
your compiler gives the same result of execution.
ADDING THE KEYWORD VIRTUAL
Example program > VIRTUAL2.CPP
As you examine the next example program named VIRTUAL2.CPP, you will
notice that there is one small change in line 9. The keyword virtual
has been added to the declaration of the method named message() in
the parent class.
It may be a bit of a disappointment to you to learn that this program
operates no differently than the last example program. This is because
we are using objects directly and virtual methods have nothing to
do with objects, only with pointers to objects as we will see soon.
There is an additional comment in line 50 illustrating that since
all four objects are of different classes, it is impossible to assign
any object to any other object in this program. We will soon see that
some pointer assignments are permitted between objects of dissimilar
classes.
After you are sure that the fact that they are virtual functions,
or methods, has nothing to do with the objects as they are instantiated,
compile and execute this example program to see if your compiler results
in the same output as that listed.
USING OBJECT POINTERS
Example program > VIRTUAL3.CPP
Examine the example program named VIRTUAL3.CPP and you will find a
repeat of the first program but with a different main program.
In this program the keyword virtual has been removed from the method
declaration in the parent class in line 9, and the main program defines
pointers to the objects rather than defining the objects themselves
in lines 41 through 44. Since we only defined pointers to the objects,
we find it necessary to allocate the objects before using them by
using the new operator in lines 46 through 53. Upon running the program,
we find that even though we are using pointers to the objects we have
done nothing different than what we did in the first program. Upon
execution, we find that the program operates in exactly the same manner
as the first example program in this chapter. This should not be surprising
because a pointer to a method can be used to operate on an object
in the same manner as an object can be manipulated.
Be sure to compile and execute this program before continuing on to
the next example program. The observant student will notice that we
failed to check the allocation to see that it did allocate the objects
properly, and we also failed to deallocate the objects prior to terminating
the program. As always, in such a simple program, it doesn't matter
because the heap will be cleaned up automatically when we return to
the operating system.
A POINTER AND A VIRTUAL FUNCTION
Example program > VIRTUAL4.CPP
The example program named VIRTUAL4.CPP is identical to the last program
except for the addition of the keyword virtual to line 9 once again.
I hope you are not terribly disappointed to find that this program,
including the keyword virtual, is still identical to the last program.
Once again we are simply using pointers to each of the objects, and
in every case the pointer is of the same type as the object to which
it points. You will begin to see some changes in the next example
program, so be patient, we are almost there.
Once again, it would be best for you to compile and execute this program.
The four previous programs were meant to instruct you in what virtual
functions do not do. The next two will show you what virtual functions
do.
A SINGLE POINTER TO THE PARENT CLASS
Example program > VIRTUAL5.CPP
Examine the example program named VIRTUAL5.CPP where we almost use
a virtual method. Be just a little patient because we are almost ready
to use a virtual method.
You will notice that this is another copy of our program with the
keyword virtual omitted from line 9 and with a totally different main
program. In this program, we only define a single pointer to a class
and the pointer is pointing to the base class of the class hierarchy.
We will use the single pointer to refer to each of the four classes
and observe what the output of the method named message() is.
A little digression is in order to understand how we can use a pointer
which has been declared to point to one class, to actually refer to
another class. If we referred to a vehicle (in the real world, not
necessarily in this program), we could be referring to a car, a truck,
a motorcycle, or any other kinds of transportation, because we are
referring to a very general form of an object. If however, we were
to refer to a car, we are excluding trucks, motorcycles, and all other
kinds of transportation, because we are referring to a car specifically.
The more general term of vehicle can therefore refer to many kinds
of vehicles, but the more specific term of car can only refer to a
single kind of vehicle, namely a car.
We can apply the same thought process in C++ and say that if we have
a pointer to a vehicle, we can use that pointer to refer to any of
the more specific objects, and that is indeed legal in C++ according
to the definition of the language. In a like manner, if we have a
pointer to a car, we cannot use that pointer to reference any of the
other classes including the vehicle class because the pointer to the
car class is too specific and restricted to be used on any of the
other classes.
THE C++ POINTER RULE
The rule as given in C++ terms is as follows. A pointer declared as
pointing to a base class can be used to point to an object of a derived
class of that base class, but a pointer to a derived class cannot
be used to point to an object of the base class or to any of the other
derived classes of the base class. In our program therefore, we are
allowed to declare a pointer to the vehicle class which is the base
class, and use that pointer to refer to objects of the base class
or any of the derived classes.
This is exactly what we do in the main program. We define a single
pointer which points to the vehicle class and use it to point to objects
of each of the classes in the same order as in the last four programs.
In each case, we allocate the object, send a message to the method
named message() and deallocate the object before going on to the next
class. You will notice that when we send the four messages, we are
sending the message to the same method, namely the method named message()
which is a part of the vehicle base class. This is because the pointer
has a class associated with it. Even though the pointer is actually
pointing to four different classes in this program, the program acts
as if the pointer is always pointing to an object of the base class
because the pointer is of the type of the base class.
The next program will finally do something you have not seen in any
C program or in any C++ program in this tutorial up to this point.
After you compile and execute the current program, we will go on to
study our first virtual function.
AN ACTUAL VIRTUAL FUNCTION
Example program > VIRTUAL6.CPP
We finally come to an example program with a virtual function that
operates as a virtual function and exhibits dynamic binding or polymorphism
as it is called. This is in the program named VIRTUAL6.CPP.
This program is identical to the last example program except that
the keyword virtual is added to line 9 to make the method named message()
a virtual function. You will notice that the keyword virtual only
appears in the base class, all classes that derive this class will
have the corresponding method automatically declared virtual by the
system. In this program, we will once again use the single pointer
to the base class and allocate, use, then delete an object of each
of the four available classes using the identical code we used in
the last program. However, because of the addition of the keyword
virtual in line 9, this program acts entirely different from the last
example program.
Since the method named message() is declared to be a virtual method
in its declaration in the base class, anytime we refer to this method
with a pointer to the base class, we actually execute the method associated
with one of the derived classes. But this is true only if there is
a method available in the derived class and if the pointer is actually
pointing to that derived class. When the program is executed, the
output reflects the same output we saw in the other cases when we
were actually calling the methods in the derived classes, but now
we are using a pointer of the base class type to make the calls.
You will notice that in lines 44, 48, 52, and 56, even though the
code is identical in each line, the system is making the decision
of which method to actually call based on the type of the pointer
when each message is sent. The decision of which method to call is
not made during the time when the code is compiled but when the code
is executed. This is dynamic binding and can be very useful in some
programming situations. In fact, there are only three different calls
made because the class named truck does not have a method named message(),
so the system simply uses the method from the base class to satisfy
the message passed. For this reason, a virtual function must have
an implementation available in the base class which will be used if
one is not available in one or more of the derived classes. Note that
the message is actually sent to a pointer to the object, but this
is splitting hairs and should not be overly emphasized at this time.
It is probably not obvious, but the observant student will note that
the structure of the virtual function in the base class and each of
the derived classes is identical. The return type and the number and
types of the parameters must be identical for all functions, since
a single statement can be used to call any of them.
IS THIS REALLY SIGNIFICANT?
This program probably does not seem to do much when you first approach
it, but the dynamic binding is a very useful construct and will be
illustrated in the next chapter with a rather simple program that
uses the technique of dynamic binding to implement a personnel list
for a small company.
If the keyword virtual is used, the system will use late binding which
is done at run time, but if the keyword is not included, early binding
will be used. What these words actually mean is that with late binding,
the compiler does not know which method will actually respond to the
message because the type of the pointer is not known at compile time.
With early binding, however, the compiler decides at compile time
what method will respond to the message sent to the pointer.
Be sure to compile and execute this example program before continuing
on to the next chapter where we will see a practical example of the
use of this technique.
PROGRAMMING EXERCISES
- Modify
VIRTUAL3.CPP to deallocate the objects prior to terminating the
program.
- Add
a message() method to the truck class of VIRTUAL6.CPP to observe
the use of the new method instead of defaulting to the parent
class method.
<< back next >>
[Introduction] [Chapter 1] [Chapter
2] [Chapter 3] [Chapter
4] [Chapter 5] [Chapter
6] [Chapter 7] [Chapter
8] [Chapter 9] [ Chapter
10 ] [Chapter 11] [Chapter
12] |
|