Our file of employees now has two different types. Those that get a flat wage and those that get an hourly wage. Those with the hourly wage have their pay computed by multiplying hours times 6.75. The flat wage people all get $567.32. We want to print the name of each employee along with the wages earned. File entries for each type of person will be different as will the methode for computing the wages. What are the functions we will need for all types of employee?
When we have more than one class that shares the same set of functions, we create a single class that has all of the properties of each class we are modeling. That single class is called the parent class. Each of the actual classes we are constructing is a child of the parent. In our example, Employee is the parent. The classes HourlyEmployee and FlatWageEmployee are the children. Notice that while all employees are either HourlyEmployees or FlatWageEmployees, none of the employees are just employees. These parent classes are also called abstract base classes, since there will never be an object in this class. The hierarchy of classes can be represented by the diagram below.
Last week we found that our hierachy didn't work very well because when the compiler creates code for normal function calls, it always fixes the actual function to the object used in the call. So if write was called from an Employee, the function call was fixed to call the write from the class Employee. This made our Hierarchy a nice organizational tool with no practical application. To overcome this we introduced pointers (in our case counted pointers) and virtual functions. This week we will develop the problem using virtual functions and pointers.
To make a function virtual you need only add the reserved word virtual before the declaration of the function. Here is the definition of HourlyEmployee.
class HourlyEmployee :public Employee
{
public:
HourlyEmployee();
HourlyEmployee(const string &,int);
virtual void write (ostream&);
virtual void read(istream&);
virtual void print();
private:
string _lastName;
string _firstName;
int _hours;
};
It also helps to clarify our programs if we specify a type name for our pointers, so you will see the line:
typedef RcPointer< HourlyEmployee> HourlyPtr;The reserved word typedef can be used to create names for complex types. In the long run we will find than frequently we need to create a new class instead of a typedef. For more details click here.
One thing to note here is that you cannot read a string with spaces included in it using the extract operator >>. To read a string with included spaces you must use the function getline. This function can be used to read from a file as follows:
getline(in,_name,'\n');
if(_name =="")
getline(in,_name,'\n');
This code also clears a leftover end of line character in the case you have read a number prior to reading this string.
One other change you will notice is that the trivial functions in the class Employee have been replaced by the symbols =0. This notation indicates that these functions are not defined because they are intended only as place holders, and no object will ever be of type Employee, only a descendent of Employee. Such functions are purely virtual functions and make the class Employee an abstract class. Once yu have created a purely virtual function in a class, the compiler will no longer allow you to create an object in this class. Abstract classes serve only as a unifying force for a collection of related classes.
As promised last week, we can use counted pointers in conjunction with virtual functions to simplify the code used to read our file. The original code used to read the file contained this section:
int type;
in>>type;
if (type == Employee::HOURLY)
{
HourlyEmployee e;
e.read(in);
e.print();
cout << endl;
}
else
{
FlatRateEmployee e;
e.read(in);
e.print();
cout << endl;
}
If we use pointers and virtual functions we can replace this section with:
int type; in >> type; EmployeePtr e; if (type == Employee::HOURLY) e = new HourlyEmployee; else e = new FlatRateEmployee; e->read(in); e->print(); cout << endl;
We might be able to make improvements to the version of run that creates the file, but these would be minor. For details on the program click here
We are now ready to create a program from a problem description. The grocery list problem will be used for the next few weeks, so is described in the file you can get by clicking here.