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 method 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 FlatRateEmployee are the children. Notice that while all employees are either HourlyEmployees or FlatRateEmployees, 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.
This hierarchy can be implemented by defining the class Employee first and following this define the classes HourlyEmployee and FlatRateEmployee. To indicate that these classes are children of Employee, the first line of their definitions contain :public Employee. This may be pleasing from an organizational standpoint, but one look at the solution will show it is far from pleasing from a programming standpoint.
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;
}
It would seem that the we did a lot of work creating a hierarchy with no pay back. It would be nice if we could define a single variable Employee and somehow or another make sure that when we initialized that employee we created the correct type. If you were to execute the following code:
Employee e; e.read(in); e.print(in); cout << endl;no matter how we initialized e, the call e.read(in) calls the read for Employee (the empty function). The compiler will not permit you to create an HourlyEmployee and assign it to an Employee because of strict typing, so you cannot do the following:
HourlyEmployee h; Employee e; e = h; //This is a compiler error e.read(in); e.print(in); cout << endl;
What can we do? There is one place strict typing is relaxed. If you have a pointer to a child, it can be assigned to a variable that has type pointer to its parent. So if we create a pointer to HourlyEmployee then that pointer could be assigned to e and e would then call HourlyEmployee functions. Pointers in c++ are notoriously difficult to work with so we will introduce an extension of c++, the counted pointer.
The class counted pointer is defined in the header rcpointe.h. This is a template class that allows you define pointers to any type of object you wish. Our section of code could be rewritten in this way:
RcPointer< Employee e>= = new HourlyEmployee; e->read(in); e->print(in); cout << endl;Note the arrow (->) used for dereferencing. This would eliminate the compiler error, but you would still be calling the read and print functions for Employee. For normal functions the compiler decides at compile time that since e is a pointer to an Employee, it should use the functions for Employee.