Coronado Enterprises C++ TUTOR: Chapter 11
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
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.
PERSON.H
// Chapter 11 - Program 1
#ifndef PERSON_H
#define PERSON_H
class person {
protected: // Make these variables available to the subclasses
char name[25];
int salary;
public:
virtual void display(void);
};
#endif
THE PERSON IMPLEMENTATION
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 to 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.
PERSON.CPP
// Chapter 11 - Program 2
#include <iostream.h>
#include "person.h"
// This method should never be called. If it is ever
// called, it is considered an error.
void
person::display(void)
{
cout << "person::display - missing subclass method\n";
}
THE SUPERVISOR HEADER
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() and 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 will 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.
SUPERVSR.H
// Chapter 11 - Program 3
#ifndef SUPERVSR_H
#define SUPERVSR_H
// This defines three subclasses of the parent type person. Different
// data is stored for the different job classifications to illustrate
// that it can be done.
#include "person.h"
class supervisor : public person {
char title[25];
public:
void init_data(char in_name[], int in_salary, char in_title[]);
void display(void);
};
class programmer : public person {
char title[25];
char language[25];
public:
void init_data(char in_name[], int in_salary, char in_title[],
char in_language[]);
void display(void);
};
class secretary : public person {
char shorthand;
int typing_speed;
public:
void init_data(char in_name[], int in_salary,
char in_shorthand, char in_typing_speed);
void display(void);
};
#endif
THE SUPERVISOR IMPLEMENTATION
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.
SUPERVSR.CPP
// Chapter 11 - Program 4
#include "supervsr.h"
#include <iostream.h>
#include <stdio.h>
#include <string.h>
// In all cases, init_data assigns values to the class variables and
// display outputs the values to the monitor for inspection.
void
supervisor::init_data(char in_name[], int in_salary, char in_title[])
{
strcpy(name,in_name);
salary = in_salary;
strcpy(title, in_title);
}
void
supervisor::display(void)
{
cout << "Supervisor --> " << name << "'s salary is " << salary <<
" and is the " << title << ".\n\n";
}
void
programmer::init_data(char in_name[], int in_salary, char in_title[],
char in_language[])
{
strcpy(name,in_name);
salary = in_salary;
strcpy(title, in_title);
strcpy(language, in_language);
}
void
programmer::display(void)
{
cout << "Programmer --> " << name << "'s salary is " << salary <<
" and is " << title << ".\n";
cout << " " << name << "'s specialty is " <<
language << ".\n\n";
}
void
secretary::init_data(char in_name[], int in_salary,
char in_shorthand, char in_typing_speed)
{
strcpy(name,in_name);
salary = in_salary;
shorthand = in_shorthand;
typing_speed = in_typing_speed;
}
void
secretary::display(void)
{
cout << "Secretary ---> " << name << "'s salary is " << salary <<
".\n";
cout << " " << name << " types " << typing_speed <<
" per minute and can ";
if (!shorthand) cout << "not ";
cout << "take shorthand.\n\n";
}
THE FIRST CALLING PROGRAM
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 and 42, 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 42, we actually send messages to each of the three methods named display() in the subclasses. 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.
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.
EMPLOYEE.CPP
// Chapter 11 - Program 5
#include <iostream.h>
#include "person.h"
#include "supervsr.h"
person *staff[10];
main()
{
supervisor *suppt;
programmer *progpt;
secretary *secpt;
cout << "XYZ Staff -- note salary is monthly.\n\n";
suppt = new supervisor;
suppt->init_data("Big John", 5100, "President");
staff[0] = suppt;
progpt = new programmer;
progpt->init_data("Joe Hacker", 3500, "debugger", "Pascal");
staff[1] = progpt;
progpt = new programmer;
progpt->init_data("OOP Wizard", 7700, "senior analyst", "C++");
staff[2] = progpt;
secpt = new secretary;
secpt->init_data("Tillie Typer", 2200, 1, 85);
staff[3] = secpt;
suppt = new supervisor;
suppt->init_data("Tom talker", 5430, "Sales manager");
staff[4] = suppt;
progpt = new programmer;
progpt->init_data("Dave Debugger", 5725, "code maintainer",
"assembly language");
staff[5] = progpt;
for (int index = 0 ; index < 6 ; index++ )
staff[index]->display();
cout << "End of employee list.\n";
}
// Result of execution
// XYZ Staff -- note salary is monthly.
//
// Supervisor --> Big John's salary is 5100 and is the President.
//
// Programmer --> Joe Hacker's salary is 3500 and is debugger.
// Joe Hacker's specialty is Pascal.
//
// Programmer --> OOP Wizard's salary is 7700 and is senior analyst.
// OOP Wizard's specialty is C++.
//
// Secretary ---> Tillie Typer's salary is 2200.
// Tillie typer types 85 per minute and can take shorthand.
//
// Supervisor --> Tom Talker's salary is 5430 and is the sales manager.
//
// Programmer --> Dave Debugger's salary is 5725 and is code maintainer.
// Dave Debugger's specialty is assembly language.
//
// End of employee list.
A PURE VIRTUAL FUNCTION
The pure virtual function is also available in the C++ toolbox of possible constructs. You can use a pure virtual function in the present example program by changing line 10 of PERSON.H to read as follows;
virtual void display(void) = 0;
You must then eliminate PERSON.CPP from the project or make sequence. An implementation for a pure virtual function cannot exist in the base class.
Every derived class must include a function for each pure virtual function which is inherited into the derived class. This assures that there will be a function available for each call and none will ever need to be answered by the base class. You are not permitted to 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.
THE LINKED LIST CLASS
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 define it. The complete declaration for the class is given in lines 22 through 29. The second construct of interest is the friend class listed in line 17 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 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 open so we need an implementation for this class.
ELEMLIST.H
// Chapter 11 - Program 6
#ifndef ELEMLIST_H
#define ELEMLIST_H
#define NULL 0
#include "person.h"
class employee_list; // Forward declaration
class employee_element { // One element of the linked list
person *employee_data;
employee_element *next_employee;
public:
employee_element(person *new_employee)
{next_employee = NULL;
employee_data = new_employee;};
friend class employee_list;
};
class employee_list { // The linked list
employee_element *start;
employee_element *end_of_list;
public:
employee_list() {start = NULL;}
void add_person(person *new_employee);
void display_list(void);
};
#endif
THE LINKED LIST IMPLEMENTATION
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 a sorting mechanism could be added to sort the employees by name if desired.
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, 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.
ELEMLIST.CPP
// Chapter 11 - Program 7
#include "elemlist.h"
void
employee_list::add_person(person *new_employee)
{
employee_element *temp;
temp = new employee_element(new_employee);
if (start == NULL)
start = end_of_list = temp;
else {
end_of_list->next_employee = temp;
end_of_list = temp;
}
}
void
employee_list::display_list(void)
{
employee_element *temp;
temp = start;
do {
temp->employee_data->display();
temp = temp->next_employee;
} while (temp != NULL);
}
THE LINKED LIST IMPLEMENTATION
At this time you should examine the final example program in this chapter 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 declare 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.
If you changed PERSON.H to use a pure virtual function, it will still work with this program just as we discussed earlier.
EMPLOYE2.CPP
// Chapter 11 - Program 8
#include <iostream.h>
#include "person.h"
#include "supervsr.h"
#include "elemlist.h"
employee_list list;
main()
{
supervisor *suppt;
programmer *progpt;
secretary *secpt;
cout << "XYZ Staff -- note salary is monthly.\n\n";
suppt = new supervisor;
suppt->init_data("Big John", 5100, "President");
list.add_person(suppt);
progpt = new programmer;
progpt->init_data("Joe Hacker", 3500, "debugger", "Pascal");
list.add_person(progpt);
progpt = new programmer;
progpt->init_data("OOP Wizard", 7700, "senior analyst", "C++");
list.add_person(progpt);
secpt = new secretary;
secpt->init_data("Tillie Typer", 2200, 1, 85);
list.add_person(secpt);
suppt = new supervisor;
suppt->init_data("Tom talker", 5430, "Sales manager");
list.add_person(suppt);
progpt = new programmer;
progpt->init_data("Dave Debugger", 5725, "code maintainer",
"assembly language");
list.add_person(progpt);
// Now display the entire list
list.display_list();
cout << "End of employee list.\n";
}
// Result of execution
// XYZ Staff -- note salary is monthly.
//
// Supervisor --> Big John's salary is 5100 and is the President.
//
// Programmer --> Joe Hacker's salary is 3500 and is debugger.
// Joe Hacker's specialty is Pascal.
//
// Programmer --> OOP Wizard's salary is 7700 and is senior analyst.
// OOP Wizard's specialty is C++.
//
// Secretary ---> Tillie Typer's salary is 2200.
// Tillie typer types 85 per minute and can take shorthand.
//
// Supervisor --> Tom Talker's salary is 5430 and is the sales manager.
//
// Programmer --> Dave Debugger's salary is 5725 and is code maintainer.
// Dave Debugger's specialty is assembly language.
//
// End of employee list.
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 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 does not 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.
PROGRAMMING EXERCISES
1. 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.