|
 |
[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 11: More Virtual Functions
This chapter will actually be a continuation of the topics covered
in the last chapter but this will be a fuller explanation of what
virtual functions are and how they can be used in a program. We will
present a simple database program with a virtual function to show
how it can be used, then we will go on to illustrate a more complex
use of the virtual function in a manner that finally illustrates its
utility and reason for existence.
HOW TO START AN OOP PROJECT
The observant student will notice that we begin our use of object
oriented programming by identifying an object, or in this case, a
class of objects and even some subordinate objects, which we completely
define. When we get to the main program we then have a simple job
with the remaining needs and they are completed using standard procedural
programming techniques which we are familiar with. This is the way
to begin any object oriented programming project, by first identifying
a few objects that can be separated conveniently from the rest of
the code, programming them, then writing the main program. It should
be added that, for your first project using objects, do not try to
make everything an object. Select a few objects and after gaining
experience with object oriented programming techniques, use more objects
on future projects. Most programmers use too many objects for their
first project and write very obtuse, unreadable code.
THE PERSON HEADER FILE
Example program > PERSON.H
Examine the file named PERSON.H for the definition file for the person
class. This class definition should cause you no problem to understand
since there is nothing new here. The only thing that should be mentioned
about this class is that the protected mode is used for the variables
so that they are readily available in the derived classes which will
inherit this class. Notice that the one method in this class, line
11, is declared virtual.
THE PERSON IMPLEMENTATION
Example program strong> PERSON.CPP
The implementation for the person class is given here and it is a
little strange in the way it is written and used. The intent of this
program is that the virtual method named display() in this file will
never be used, but it is required by the C++ compiler so it can be
used for a default in case some of the subclasses do not have this
function available. In the main program we will be careful to never
call this function due to the nature of the program we are writing.
Keep in mind that C++ requires an implementation of all virtual functions
even if they are never used. In this case the message is obviously
intended to be output as an error message.
Be sure to compile this program prior to going on to the next class
definitions.
THE SUPERVISOR HEADER
Example program > SUPERVSR.H
The file named SUPERVSR.H contains the class definitions for the three
derived classes, supervisor, programmer, and secretary. These were
all placed in a single file for two reasons. The first reason is to
simply illustrate to you that this can be done, and secondly, to allow
some of the files to be combined on the disk and to require fewer
compilations by you prior to executing the resulting program. This
is actually a good way to combine these files since they are all derived
classes of a common class. It is a matter of style or personal taste.
You will notice that all three of these classes contain a method named
display(), all have the same return value of void, and all have the
same number of parameters as the parent class's method of the same
name. All of this equality is required because they can all be called
by the same call statement. You will also notice that the other method
in each class has the same name, but different numbers and types of
formal parameters which prevents this method from being used as a
virtual method.
The remainder of this file is simple and you should be able to read
the code and understand it completely. Once again, this file cannot
be compiled or executed.
THE SUPERVISOR IMPLEMENTATION
Example program > SUPERVSR.CPP
The file named SUPERVSR.CPP contains the implementation for the three
classes. If you spend a little time studying the code, you will find
that each of the methods named init_data() simply initializes all
fields to those passed in as the actual arguments in a very simple
manner.
The method named display(), however, outputs the stored data in different
ways for each class since the data is so different in each of the
classes. Even though the interface to these three methods is identical,
the actual code is significantly different. There is no reason code
besides output could not have been used, but the output is so visible
when the program is executed that it was chosen for this illustration.
This file should be compiled at this time in preparation for the next
example program which will use all four classes as defined in these
four files.
THE FIRST CALLING PROGRAM
Example program strong> EMPLOYEE.CPP
The file named EMPLOYEE.CPP is the first program that uses the classes
developed in this chapter, and you will find that it is a very simple
program.
We begin with an array of ten pointers, each pointing to the base
class. As you recall from the last chapter, this is very important
when using virtual functions, the pointer must point to the base class.
The pointers that will be stored in this array will all point to objects
of the derived classes however. When we use the resulting pointers
to refer to the methods, the system will choose the method at run
time, not at compile time as nearly all of our other programs have
been doing.
We allocate six objects in lines 16 through 39, initialize them to
some values using the methods named init_data(), then assign the pointers
to the members of the array of pointers to person. Finally, in lines
41 through 44, we call the methods named display() to display the
stored data on the monitor. You will notice that even though we only
use one method call in line 43, we actually send messages to each
of the three methods named display() in the derived classes. This
is true dynamic binding because if we were to change the values of
some of the pointers in the array, we would then call different methods
with the same pointers.
In order for this to work, the interface had to be identical for each
function, but the implementation could be different for each function.
Polymorphism consists of two root words, poly and morph. Poly means
different and refers to the implementation, and morph means the same
which refers to the interface.
Be sure to compile and execute this program before continuing on in
this chapter. You will recall that the linking step requires you to
combine several files in order to satisfy all system calls. After
you have done that, we will use the same objects in another way to
show how they can be reused.
THE LINKED LIST CLASS
Example program > ELEMLIST.H
Examination of the file named ELEMLIST.H will reveal the definition
of two more classes which will be used to build a linked list of employees
to illustrate a more practical way to use the dynamic binding we have
been studying in this chapter.
The two classes were put in the same file because they work together
so closely and neither is of much value without the other. You will
notice that the elements of the linked list do not contain any data,
only a pointer to the person class that we developed for the last
program, so that the linked list will be composed of elements of the
person class without modifying the class itself.
There are two interesting constructs used here that must be pointed
out before going on to the next program. The first is the partial
declaration given in line 8 which allows us to refer to the class
named employee_list before we actually declare it. The complete declaration
for the class is given in lines 23 through 31. The second construct
of interest is the friend class listed in line 18 where we give the
entire class named employee_list free access to the variables which
are a part of the employee_element class. This is necessary because
the method named add_person() must access the pointers contained in
employee_element. We could have defined an additional method as a
part of employee_element and used this method to refer to the pointers
but it was felt that these two classes work so well together that
it is not a problem to open a window of visibility between the classes.
We still have complete privacy from all other programs and classes
declared as parts of this program.
Note that the single method included in the employee_element class
is implemented in inline code. Two of the methods of employee_list
are still undefined so we need an implementation for this class.
THE LINKED LIST IMPLEMENTATION
Example program > ELEMLIST.CPP
The file named ELEMLIST.CPP is the implementation for the linked list
classes and should be self explanatory if you understand how a singly
linked list operates. All new elements are added to the end of the
current list. This was done to keep it simple but an alphabetic sorting
mechanism could be added to sort the employees by name if desired.
This will be used to store a list of employees as described earlier
in this chapter. You will notice that if memory cannot be allocated,
the program simply exits. This is not acceptable for a production
program that must be competitive in the marketplace. Error recovery
is a major topic and one you will need to study someday.
The method to display the list simply traverses the list and calls
the method named display() in line 30 once for each element of the
list.
It is important for you to take notice that in this entire class,
there is no mention made of the existence of the three derived classes
which we desribed earlier in this chapter, only the base class named
person is mentioned. The linked list therefore has no hint that the
three subclasses even exist, but in spite of that, we will see this
class send messages to the three subclasses as they are passed through
this logic. That is exactly what dynamic binding is, and we will have
a little more to say about it after we examine the calling program.
USING THE LINKED LIST
Example program > EMPLOYE2.CPP
At this time you should examine the example program named EMPLOYE2.CPP
for our best example of dynamic binding in this tutorial, yet the
program is kept very simple.
This program is very similar to the example program named EMPLOYEE.CPP
with a few changes to better illustrate dynamic binding. In line 7
we define an object of the class employee_list to begin our linked
list. This is the only copy of the list we will need for this program.
For each of the elements, we allocate the data, fill it, and send
it to the linked list to be added to the list where we allocate another
linked list element to point to the new data, and add it to the list.
The code is very similar to the last program down through line 40.
In line 43 we send a message to the display_list() method which outputs
the entire list of personnel. You will notice that the linked list
class defined in the files named ELEMLIST.H and ELEMLIST.CPP are never
informed in any way that the subclasses even exist but they dutifully
pass the pointers to these subclasses to the correct methods and the
program runs as expected.
WHAT GOOD IS ALL OF THIS?
Now that we have the program completely debugged and working, suppose
that we wished to add another class to the program. For example, suppose
we wished to add a class named consultant because we wished to include
some consultants in our business.
We would have to write the class of course and the methods within
the classes, but the linked list doesn't need to know that the new
class is added, so it doesn't require any changes in order to update
the program to handle consultant class objects. In this particular
case, the linked list is very small and easy to understand, but suppose
the code was very long and complex as with a large database. It would
be very difficult to update every reference to the subclasses and
add another subclass to every list where they were referred to, and
this operation would be very error prone. In the present example program,
the linked list would not even have to be recompiled in order to add
the new functionality.
It should be clear to you that it would be possible to actually define
new types, dynamically allocate them, and begin using them even while
the program was executing if we properly partitioned the code into
executable units operating in parallel. This would not be easy, but
it could be done for a large database that was tracking the inventory
for a large retail store, or even for an airlines reservation system.
You probably have little difficulty understanding the use of dynamically
allocated memory for data, but dynamically allocating classes or types
is new and difficult to grasp, but the possibility is there with dynamic
binding.
If you were very alert, you noticed that we have no provision to deallocate
either the elements of the list or the list itself. To add it, we
would need to add a method to the employee_list class, possibly named
delete_person(), and add a destructor to the employee_data class .
AN APPLICATION FRAMEWORK
Example program > APPFRAM1.CPP
The example program named APPFRAM1.CPP illustrates the method used
to write an application framework. If you do much serious programming,
you will encounter one because they provide so many benefits for the
MS Windows programmer. There are application frameworks available
for other operating systems too, so most programmers should be familiar
with them.
The class named CForm is the base class for our trivial but important
example, and it consists of four methods but no data members. The
diligent student will note that the method named display_form calls
the other three members to actually do the work of displaying our
little form on the monitor. There is nothing magic about this program
except that it is the framework for a very interesting concept used
by all of the application frameworks currently available. Note that
three of the methods are declared as virtual in lines 9 through 11.
The interesting part occurs when we inherit the class into our new
class named CMyForm in line 27 and write new methods for two of the
base class methods. We have inherited as much functionality from the
base class as we liked, and written new methods for those that didn't
serve our purpose as originally written. When we finally execute an
object of the new class in line 42 of the main program, we do indeed
use part of the base class, and override those parts that we explicitly
wrote methods for.
This is not too appealing in such a simple example, but if we consider
how this is used in a real application framework, it is a very useful
construct. The writer of an application framework will write a complete
program that does all of the necessary housekeeping and windows maintenance
chores and partition it in a number of virtual methods much like we
did here. Of course, the framework will be composed of a large body
of code to do all of those chores. In much the same manner that we
picked which parts of this little display program we wished to use,
and which to override, we pick those parts of the framework that we
wish to keep as is and override those parts that we wish change. A
large body of code is therefore already written for us and ready for
our use.
The application framework can include many other preprogrammed functions
ready for our use, such as editor code for edit windows, data verification
code for dialogue windows, and ready to use message window code. You
will find it to be a very rewarding study to spend time examining
the capabilities of one or more of the commercially available application
frameworks.
A PURE VIRTUAL FUNCTION
Example program > APPFRAM2.CPP
The example program named APPFRAM2.CPP illustrates a pure virtual
function. A pure virtual function is declared by assigning the value
of zero to the function as illustrated in line 10. A class containing
one or more pure virtual functions cannot be used to define an object.
The class is therefore only useful as a base class to be inherited
into a useable derived class. It is sometimes called an abstract class.
Every derived class must include a function for each pure virtual
function that is inherited from the base class if it will be used
to create an object. This assures that there will be a function available
for each call and none will ever need to be answered by the base class,
which it cannot do since it does not have an implementation for a
pure virtual function. You cannot create an object of any class which
contains one or more pure virtual functions, because there is nothing
to answer a message if one is sent to the pure virtual method. The
compiler will enforce the two rules mentioned in this paragraph. If
a class inherits an abstract class without providing an override for
the pure virtual function, then it too becomes an abstract class and
cannot be used to define an object.
You will notice that the present example uses an abstract base class
which makes it illegal to use an object of the base class as we did
in the last example program. For that reason, some of the main program
is commented out.
You will find abstract classes used in many commercially available
libraries and in application frameworks. Be sure to compile and execute
this program, then make some modifications to see what the compiler
indicates if you try to violate some of the rules we have delineated
here.
PROGRAMMING EXERCISES
- Add
a new class named consultant to the files named SUPERVSR.H and
SUPERVSR.CPP, then add code to EMPLOYE2.CPP to exercise the new
class. Note that you do not need to recompile the linked list
class in order to execute the new code and use the new class.
Even without recompiling the linked list class it is capable of
storing and passing the new class of data provided of course that
the new class is referred to using a pointer to the parent class.
<< 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] |
|