www.gibmonks.com

Main Page




Previous Page
Next Page

[Page 710 (continued)]

13.6. Case Study: Payroll System Using Polymorphism

This section reexamines the CommissionEmployee-BasePlusCommissionEmployee hierarchy that we explored throughout Section 12.4. In this example, we use an abstract class and polymorphism to perform payroll calculations based on the type of employee. We create an enhanced employee hierarchy to solve the following problem:


[Page 711]

A company pays its employees weekly. The employees are of four types: Salaried employees are paid a fixed weekly salary regardless of the number of hours worked, hourly employees are paid by the hour and receive overtime pay for all hours worked in excess of 40 hours, commission employees are paid a percentage of their sales and base-salary-plus-commission employees receive a base salary plus a percentage of their sales. For the current pay period, the company has decided to reward base-salary-plus-commission employees by adding 10 percent to their base salaries. The company wants to implement a C++ program that performs its payroll calculations polymorphically.

We use abstract class Employee to represent the general concept of an employee. The classes that derive directly from Employee are SalariedEmployee, CommissionEmployee and HourlyEmployee. Class BasePlusCommissionEmployeederived from CommissionEmployeerepresents the last employee type. The UML class diagram in Fig. 13.11 shows the inheritance hierarchy for our polymorphic employee payroll application. Note that abstract class name Employee is italicized, as per the convention of the UML.

Figure 13.11. Employee hierarchy UML class diagram.


Abstract base class Employee declares the "interface" to the hierarchythat is, the set of member functions that a program can invoke on all Employee objects. Each employee, regardless of the way his or her earnings are calculated, has a first name, a last name and a social security number, so private data members firstName, lastName and socialSecurityNumber appear in abstract base class Employee.

Software Engineering Observation 13.10

A derived class can inherit interface or implementation from a base class. Hierarchies designed for implementation inheritance tend to have their functionality high in the hierarchyeach new derived class inherits one or more member functions that were defined in a base class, and the derived class uses the base-class definitions. Hierarchies designed for interface inheritance tend to have their functionality lower in the hierarchya base class specifies one or more functions that should be defined for each class in the hierarchy (i.e., they have the same prototype), but the individual derived classes provide their own implementations of the function(s).


The following sections implement the Employee class hierarchy. The first five each implement one of the abstract or concrete classes. The last section implements a test program that builds objects of all these classes and processes the objects polymorphically.


[Page 712]

13.6.1. Creating Abstract Base Class Employee

Class Employee (Figs. 13.1313.14, discussed in further detail shortly) provides functions earnings and print, in addition to various get and set functions that manipulate Employee's data members. An earnings function certainly applies generically to all employees, but each earnings calculation depends on the employee's class. So we declare earnings as pure virtual in base class Employee because a default implementation does not make sense for that functionthere is not enough information to determine what amount earnings should return. Each derived class overrides earnings with an appropriate implementation. To calculate an employee's earnings, the program assigns the address of an employee's object to a base class Employee pointer, then invokes the earnings function on that object. We maintain a vector of Employee pointers, each of which points to an Employee object (of course, there cannot be Employee objects, because Employee is an abstract classbecause of inheritance, however, all objects of all derived classes of Employee may nevertheless be thought of as Employee objects). The program iterates through the vector and calls function earnings for each Employee object. C++ processes these function calls polymorphically. Including earnings as a pure virtual function in Employee forces every direct derived class of Employee that wishes to be a concrete class to override earnings. This enables the designer of the class hierarchy to demand that each derived class provide an appropriate pay calculation, if indeed that derived class is to be concrete.

Function print in class Employee displays the first name, last name and social security number of the employee. As we will see, each derived class of Employee overrides function print to output the employee's type (e.g., "salaried employee:") followed by the rest of the employee's information.

The diagram in Fig. 13.12 shows each of the five classes in the hierarchy down the left side and functions earnings and print across the top. For each class, the diagram shows the desired results of each function. Note that class Employee specifies "= 0" for function earnings to indicate that this is a pure virtual function. Each derived class overrides this function to provide an appropriate implementation. We do not list base class Employee's get and set functions because they are not overridden in any of the derived classeseach of these functions is inherited and used "as is" by each of the derived classes.

Figure 13.12. Polymorphic interface for the Employee hierarchy classes.
(This item is displayed on page 713 in the print version)


Let us consider class Employee's header file (Fig. 13.13). The public member functions include a constructor that takes the first name, last name and social security number as arguments (line 12); set functions that set the first name, last name and social security number (lines 14, 17 and 20, respectively); get functions that return the first name, last name and social security number (lines 15, 18 and 21, respectively); pure virtual function earnings (line 24) and virtual function print (line 25).

Figure 13.13. Employee class header file.
(This item is displayed on pages 713 - 714 in the print version)

 1  // Fig. 13.13: Employee.h
 2  // Employee abstract base class.
 3  #ifndef EMPLOYEE_H
 4  #define EMPLOYEE_H
 5
 6  #include <string> // C++ standard string class
 7  using std::string;
 8
 9  class Employee
10  {
11  public:
12     Employee( const string &, const string &, const string & );
13
14     void setFirstName( const string & ); // set first name
15     string getFirstName() const; // return first name
16
17     void setLastName( const string & ); // set last name
18     string getLastName() const; // return last name
19
20     void setSocialSecurityNumber( const string & ); // set SSN
21     string getSocialSecurityNumber() const; // return SSN
22
23     // pure virtual function makes Employee abstract base class
24     virtual double earnings() const = 0; // pure virtual       
25     virtual void print() const; // virtual                     
26  private:
27     string firstName;
28     string lastName;
29     string socialSecurityNumber;
30  }; // end class Employee
31
32  #endif // EMPLOYEE_H

Recall that we declared earnings as a pure virtual function because we first must know the specific Employee type to determine the appropriate earnings calculations. Declaring this function as pure virtual indicates that each concrete derived class must provide an appropriate earnings implementation and that a program can use base-class Employee pointers to invoke function earnings polymorphically for any type of Employee.

Figure 13.14 contains the member-function implementations for class Employee. No implementation is provided for virtual function earnings. Note that the Employee constructor (lines 1015) does not validate the social security number. Normally, such validation should be provided. An exercise in Chapter 12 asks you to validate a social security number to ensure that it is in the form ###-##-####, where each # represents a digit.


[Page 714]

Figure 13.14. Employee class implementation file.
(This item is displayed on pages 714 - 715 in the print version)

 1  // Fig. 13.14: Employee.cpp
 2  // Abstract-base-class Employee member-function definitions.
 3  // Note: No definitions are given for pure virtual functions.
 4  #include <iostream>
 5  using std::cout;
 6
 7  #include "Employee.h" // Employee class definition
 8
 9  // constructor
10  Employee::Employee( const string &first, const string &last,
11     const string &ssn )
12     : firstName( first ), lastName( last ), socialSecurityNumber( ssn )
13  {
14     // empty body
15  } // end Employee constructor
16
17  // set first name
18  void Employee::setFirstName( const string &first )
19  {
20     firstName = first;
21  } // end function setFirstName
22
23  // return first name
24  string Employee::getFirstName() const
25  {
26     return firstName;
27  } // end function getFirstName
28
29  // set last name
30  void Employee::setLastName( const string &last )
31  {
32     lastName = last;
33  } // end function setLastName
34
35  // return last name
36  string Employee::getLastName() const
37  {
38     return lastName;
39  } // end function getLastName
40
41  // set social security number
42  void Employee::setSocialSecurityNumber( const string &ssn )
43  {
44     socialSecurityNumber = ssn; // should validate
45  } // end function setSocialSecurityNumber
46
47  // return social security number
48  string Employee::getSocialSecurityNumber() const
49  {
50     return socialSecurityNumber;
51  } // end function getSocialSecurityNumber
52
53  // print Employee's information (virtual, but not pure virtual)
54  void Employee::print() const
55  {
56     cout << getFirstName() << ' ' << getLastName()
57        << "\nsocial security number: " << getSocialSecurityNumber();
58  } // end function print


[Page 715]

Note that virtual function print (Fig. 13.14, lines 5458) provides an implementation that will be overridden in each of the derived classes. Each of these functions will, however, use the abstract class's version of print to print information common to all classes in the Employee hierarchy.

13.6.2. Creating Concrete Derived Class SalariedEmployee

Class SalariedEmployee (Figs. 13.1513.16) derives from class Employee (line 8 of Fig. 13.15). The public member functions include a constructor that takes a first name, a last name, a social security number and a weekly salary as arguments (lines 1112); a set function to assign a new nonnegative value to data member weeklySalary (lines 14); a get function to return weeklySalary's value (line 15); a virtual function earnings that calculates a SalariedEmployee's earnings (line 18) and a virtual function print that outputs the employee's type, namely, "salaried employee:" followed by employee-specific information produced by base class Employee's print function and SalariedEmployee's getWeeklySalary function (line 19).

Figure 13.15. SalariedEmployee class header file.
(This item is displayed on page 716 in the print version)

 1  // Fig. 13.15: SalariedEmployee.h
 2  // SalariedEmployee class derived from Employee.
 3  #ifndef SALARIED_H
 4  #define SALARIED_H
 5
 6  #include "Employee.h" // Employee class definition
 7
 8  class SalariedEmployee : public Employee
 9  {
10  public:
11     SalariedEmployee( const string &, const string &,
12        const string &, double = 0.0 );
13
14     void setWeeklySalary( double ); // set weekly salary
15     double getWeeklySalary() const; // return weekly salary
16
17     // keyword virtual signals intent to override               
18     virtual double earnings() const; // calculate earnings      
19     virtual void print() const; // print SalariedEmployee object
20  private:
21     double weeklySalary; // salary per week
22  }; // end class SalariedEmployee
23
24  #endif // SALARIED_H

Figure 13.16 contains the member-function implementations for SalariedEmployee. The class's constructor passes the first name, last name and social security number to the Employee constructor (line 11) to initialize the private data members that are inherited from the base class, but not accessible in the derived class. Function earnings (line 3033) overrides pure virtual function earnings in Employee to provide a concrete implementation that returns the SalariedEmployee's weekly salary. If we do not implement earnings, class SalariedEmployee would be an abstract class, and any attempt to instantiate an object of the class would result in a compilation error (and, of course, we want SalariedEmployee here to be a concrete class). Note that in class SalariedEmployee's header file, we declared member functions earnings and print as virtual (lines 1819 of Fig. 13.15)actually, placing the virtual keyword before these member functions is redundant. We defined them as virtual in base class Employee, so they remain virtual functions throughout the class hierarchy. Recall from Good Programming Practice 13.1 that explicitly declaring such functions virtual at every level of the hierarchy can promote program clarity.


[Page 717]
Figure 13.16. SalariedEmployee class implementation file.
(This item is displayed on pages 716 - 717 in the print version)

 1  // Fig. 13.16: SalariedEmployee.cpp
 2  // SalariedEmployee class member-function definitions.
 3  #include <iostream>
 4  using std::cout;
 5
 6  #include "SalariedEmployee.h" // SalariedEmployee class definition
 7
 8  // constructor
 9  SalariedEmployee::SalariedEmployee( const string &first,
10     const string &last, const string &ssn, double salary )
11     : Employee( first, last, ssn )
12  {
13     setWeeklySalary( salary );
14  } // end SalariedEmployee constructor
15
16  // set salary
17  void SalariedEmployee::setWeeklySalary( double salary )
18  {
19     weeklySalary = ( salary < 0.0 ) ? 0.0 : salary;
20  } // end function setWeeklySalary
21
22  // return salary
23  double SalariedEmployee::getWeeklySalary() const
24  {
25     return weeklySalary;
26  } // end function getWeeklySalary
27
28  // calculate earnings;
29  // override pure virtual function earnings in Employee
30  double SalariedEmployee::earnings() const
31  {
32     return getWeeklySalary();
33  } // end function earnings
34
35  // print SalariedEmployee's information
36  void SalariedEmployee::print() const
37  {
38     cout << "salaried employee: ";
39     Employee::print(); // reuse abstract base-class print function
40     cout << "\nweekly salary: " << getWeeklySalary();
41  } // end function print

Function print of class SalariedEmployee (lines 3641 of Fig. 13.16) overrides Employee function print. If class SalariedEmployee did not override print, SalariedEmployee would inherit the Employee version of print. In that case, SalariedEmployee's print function would simply return the employee's full name and social security number, which does not adequately represent a SalariedEmployee. To print a SalariedEmployee's complete information, the derived class's print function outputs "salaried employee:" followed by the base-class Employee-specific information (i.e., first name, last name and social security number) printed by invoking the base class's print using the scope resolution operator (line 39)this is a nice example of code reuse. The output produced by SalariedEmployee's print function contains the employee's weekly salary obtained by invoking the class's getWeeklySalary function.

13.6.3. Creating Concrete Derived Class HourlyEmployee

Class HourlyEmployee (Figs. 13.1713.18) also derives from class Employee (line 8 of Fig. 13.17). The public member functions include a constructor (lines 1112) that takes as arguments a first name, a last name, a social security number, an hourly wage and the number of hours worked; set functions that assign new values to data members wage and hours, respectively (lines 14 and 17); get functions to return the values of wage and hours, respectively (lines 15 and 18); a virtual function earnings that calculates an HourlyEmployee's earnings (line 21) and a virtual function print that outputs the employee's type, namely, "hourly employee:" and employee-specific information (line 22).


[Page 718]

Figure 13.17. HourlyEmployee class header file.

 1  // Fig. 13.17: HourlyEmployee.h
 2  // HourlyEmployee class definition.
 3  #ifndef HOURLY_H
 4  #define HOURLY_H
 5
 6  #include "Employee.h" // Employee class definition
 7
 8  class HourlyEmployee : public Employee
 9  {
10  public:
11     HourlyEmployee( const string &, const string &,
12        const string &, double = 0.0, double = 0.0 );
13
14     void setWage( double ); // set hourly wage
15     double getWage() const; // return hourly wage
16
17     void setHours( double ); // set hours worked
18     double getHours() const; // return hours worked
19
20     // keyword virtual signals intent to override             
21     virtual double earnings() const; // calculate earnings    
22     virtual void print() const; // print HourlyEmployee object
23  private:
24     double wage; // wage per hour
25     double hours; // hours worked for week
26  }; // end class HourlyEmployee
27
28  #endif // HOURLY_H


[Page 719]

Figure 13.18 contains the member-function implementations for class HourlyEmployee. Lines 1821 and 3034 define set functions that assign new values to data members wage and hours, respectively. Function setWage (lines 1821) ensures that wage is nonnegative, and function setHours (lines 3034) ensures that data member hours is between 0 and 168 (the total number of hours in a week). Class HourlyEmployee's get functions are implemented in lines 2427 and 3740. We do not declare these functions virtual, so classes derived from class HourlyEmployee cannot override them (although derived classes certainly can redefine them). Note that the HourlyEmployee constructor, like the SalariedEmployee constructor, passes the first name, last name and social security number to the base class Employee constructor (line 11) to initialize the inherited private data members declared in the base class. In addition, HourlyEmployee's print function calls base-class function print (line 56) to output the Employee-specific information (i.e., first name, last name and social security number)this is another nice example of code reuse.


[Page 720]
Figure 13.18. HourlyEmployee class implementation file.
(This item is displayed on pages 718 - 719 in the print version)

 1  // Fig. 13.18: HourlyEmployee.cpp
 2  // HourlyEmployee class member-function definitions.
 3  #include <iostream>
 4  using std::cout;
 5
 6  #include "HourlyEmployee.h" // HourlyEmployee class definition
 7
 8  // constructor
 9  HourlyEmployee::HourlyEmployee( const string &first, const string &last,
10     const string &ssn, double hourlyWage, double hoursWorked )
11     : Employee( first, last, ssn )
12  {
13     setWage( hourlyWage ); // validate hourly wage
14     setHours( hoursWorked ); // validate hours worked
15  } // end HourlyEmployee constructor
16
17  // set wage
18  void HourlyEmployee::setWage( double hourlyWage )
19  {
20     wage = ( hourlyWage < 0.0 ? 0.0 : hourlyWage );
21  } // end function setWage
22
23  // return wage
24  double HourlyEmployee::getWage() const
25  {
26     return wage;
27  } // end function getWage
28
29  // set hours worked
30  void HourlyEmployee::setHours( double hoursWorked )
31  {
32     hours = ( ( ( hoursWorked >= 0.0 ) && ( hoursWorked <= 168.0 ) ) ?
33        hoursWorked : 0.0 );
34  } // end function setHours
35
36  // return hours worked
37  double HourlyEmployee::getHours() const
38  {
39     return hours;
40  } // end function getHours
41
42  // calculate earnings;
43  // override pure virtual function earnings in Employee
44  double HourlyEmployee::earnings() const
45  {
46     if ( getHours() <= 40 ) // no overtime
47        return getWage() * getHours();
48     else
49        return 40 * getWage() + ( ( getHours() - 40 ) * getWage() * 1.5 );
50  } // end function earnings
51
52  // print HourlyEmployee's information
53  void HourlyEmployee::print() const
54  {
55     cout << "hourly employee: ";
56     Employee::print(); // code reuse
57     cout << "\nhourly wage: " << getWage() <<
58        "; hours worked: " << getHours();
59  } // end function print

13.6.4. Creating Concrete Derived Class CommissionEmployee

Class CommissionEmployee (Figs. 13.1913.20) derives from class Employee (line 8 of Fig. 13.19). The member-function implementations (Fig. 13.20) include a constructor (lines 915) that takes a first name, a last name, a social security number, a sales amount and a commission rate; set functions (lines 1821 and 3033) to assign new values to data members commissionRate and grossSales, respectively; get functions (lines 2427 and 3639) that retrieve the values of these data members; function earnings (lines 4346) to calculate a CommissionEmployee's earnings; and function print (lines 4955), which outputs the employee's type, namely, "commission employee:" and employee-specific information. The CommissionEmployee's constructor also passes the first name, last name and social security number to the Employee constructor (line 11) to initialize Employee's private data members. Function print calls base-class function print (line 52) to display the Employee-specific information (i.e., first name, last name and social security number).


[Page 722]
Figure 13.19. CommissionEmployee class header file.
(This item is displayed on page 720 in the print version)

 1  // Fig. 13.19: CommissionEmployee.h
 2  // CommissionEmployee class derived from Employee.
 3  #ifndef COMMISSION_H
 4  #define COMMISSION_H
 5
 6  #include "Employee.h" // Employee class definition
 7
 8  class CommissionEmployee : public Employee
 9  {
10  public:
11     CommissionEmployee( const string &, const string &,
12        const string &, double = 0.0, double = 0.0 );
13
14     void setCommissionRate( double ); // set commission rate
15     double getCommissionRate() const; // return commission rate
16
17     void setGrossSales( double ); // set gross sales amount
18     double getGrossSales() const; // return gross sales amount
19
20     // keyword virtual signals intent to override                 
21     virtual double earnings() const; // calculate earnings        
22     virtual void print() const; // print CommissionEmployee object
23  private:
24     double grossSales; // gross weekly sales
25     double commissionRate; // commission percentage
26  }; // end class CommissionEmployee
27
28  #endif // COMMISSION_H

Figure 13.20. CommissionEmployee class implementation file.
(This item is displayed on pages 720 - 721 in the print version)

 1  // Fig. 13.20: CommissionEmployee.cpp
 2  // CommissionEmployee class member-function definitions.
 3  #include <iostream>
 4  using std::cout;
 5
 6  #include "CommissionEmployee.h" // CommissionEmployee class definition
 7
 8  // constructor
 9  CommissionEmployee::CommissionEmployee( const string &first,
10     const string &last, const string &ssn, double sales, double rate )
11     : Employee( first, last, ssn )
12  {
13     setGrossSales( sales );
14     setCommissionRate( rate );
15  } // end CommissionEmployee constructor
16
17  // set commission rate
18  void CommissionEmployee::setCommissionRate( double rate )
19  {
20     commissionRate = ( ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0 );
21  } // end function setCommissionRate
22
23  // return commission rate
24  double CommissionEmployee::getCommissionRate() const
25  {
26     return commissionRate;
27  } // end function getCommissionRate
28
29  // set gross sales amount
30  void CommissionEmployee::setGrossSales( double sales )
31  {
32     grossSales = ( ( sales < 0.0 ) ? 0.0 : sales );
33  } // end function setGrossSales
34
35  // return gross sales amount
36  double CommissionEmployee::getGrossSales() const
37  {
38      return grossSales;
39  } // end function getGrossSales
40
41  // calculate earnings;
42  // override pure virtual function earnings in Employee
43  double CommissionEmployee::earnings() const
44  {
45     return getCommissionRate() * getGrossSales();
46  } // end function earnings
47
48  // print CommissionEmployee's information
49  void CommissionEmployee::print() const
50  {
51     cout << "commission employee: ";
52     Employee::print(); // code reuse
53     cout << "\ngross sales: " << getGrossSales()
54        << "; commission rate: " << getCommissionRate();
55  } // end function print

13.6.5. Creating Indirect Concrete Derived Class BasePlusCommissionEmployee

Class BasePlusCommissionEmployee (Figs. 13.2113.22) directly inherits from class CommissionEmployee (line 8 of Fig. 13.21) and therefore is an indirect derived class of class Employee. Class BasePlusCommissionEmployee's member-function implementations include a constructor (lines 1016 of Fig. 13.22) that takes as arguments a first name, a last name, a social security number, a sales amount, a commission rate and a base salary. It then passes the first name, last name, social security number, sales amount and commission rate to the CommissionEmployee constructor (line 13) to initialize the inherited members. BasePlusCommissionEmployee also contains a set function (lines 1922) to assign a new value to data member baseSalary and a get function (lines 2528) to return baseSalary's value. Function earnings (lines 3235) calculates a BasePlusCommissionEmployee's earnings. Note that line 34 in function earnings calls base-class CommissionEmployee's earnings function to calculate the commission-based portion of the employee's earnings. This is a nice example of code reuse. BasePlusCommissionEmployee's print function (lines 3843) outputs "base-salaried", followed by the output of base-class CommissionEmployee's print function (another example of code reuse), then the base salary. The resulting output begins with "base-salaried commission employee:" followed by the rest of the BasePlusCommissionEmployee's information. Recall that CommissionEmployee's print displays the employee's first name, last name and social security number by invoking the print function of its base class (i.e., Employee)yet another example of code reuse. Note that BasePlusCommissionEmployee's print initiates a chain of functions calls that spans all three levels of the Employee hierarchy.


[Page 723]
Figure 13.21. BasePlusCommissionEmployee class header file.
(This item is displayed on page 722 in the print version)

 1  // Fig. 13.21: BasePlusCommissionEmployee.h
 2  // BasePlusCommissionEmployee class derived from Employee.
 3  #ifndef BASEPLUS_H
 4  #define BASEPLUS_H
 5
 6  #include "CommissionEmployee.h" // CommissionEmployee class definition
 7
 8  class BasePlusCommissionEmployee : public CommissionEmployee
 9  {
10  public:
11     BasePlusCommissionEmployee( const string &, const string &,
12        const string &, double = 0.0, double = 0.0, double = 0.0 );
13
14     void setBaseSalary( double ); // set base salary
15     double getBaseSalary() const; // return base salary
16
17     // keyword virtual signals intent to override                         
18     virtual double earnings() const; // calculate earnings                
19     virtual void print() const; // print BasePlusCommissionEmployee object
20  private:
21     double baseSalary; // base salary per week
22  }; // end class BasePlusCommissionEmployee
23
24  #endif // BASEPLUS_H

Figure 13.22. BasePlusCommissionEmployee class implementation file.

 1  // Fig. 13.22: BasePlusCommissionEmployee.cpp
 2  // BasePlusCommissionEmployee member-function definitions.
 3  #include <iostream>
 4  using std::cout;
 5
 6  // BasePlusCommissionEmployee class definition
 7  #include "BasePlusCommissionEmployee.h"
 8
 9  // constructor
10  BasePlusCommissionEmployee::BasePlusCommissionEmployee(
11     const string &first, const string &last, const string &ssn,
12     double sales, double rate, double salary )
13     : CommissionEmployee( first, last, ssn, sales, rate )
14  {
15     setBaseSalary( salary ); // validate and store base salary
16  } // end BasePlusCommissionEmployee constructor
17
18  // set base salary
19  void BasePlusCommissionEmployee::setBaseSalary( double salary )
20  {
21     baseSalary = ( ( salary < 0.0 ) ? 0.0 : salary );
22  } // end function setBaseSalary
23
24  // return base salary
25  double BasePlusCommissionEmployee::getBaseSalary() const
26  {
27      return baseSalary;
28  } // end function getBaseSalary
29
30  // calculate earnings;
31  // override pure virtual function earnings in Employee
32  double BasePlusCommissionEmployee::earnings() const
33  {
34      return getBaseSalary() + CommissionEmployee::earnings();
35  } // end function earnings
36
37  // print BasePlusCommissionEmployee's information
38  void BasePlusCommissionEmployee::print() const
39  {
40     cout << "base-salaried ";
41     CommissionEmployee::print(); // code reuse
42     cout << "; base salary: " << getBaseSalary();
43  } // end function print


[Page 724]

13.6.6. Demonstrating Polymorphic Processing

To test our Employee hierarchy, the program in Fig. 13.23 creates an object of each of the four concrete classes SalariedEmployee, HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee. The program manipulates these objects, first with static binding, then polymorphically, using a vector of Employee pointers. Lines 3138 create objects of each of the four concrete Employee derived classes. Lines 4351 output each Employee's information and earnings. Each member-function invocation in lines 4351 is an example of static bindingat compile time, because we are using name handles (not pointers or references that could be set at execution time), the compiler can identify each object's type to determine which print and earnings functions are called.


[Page 727]
Figure 13.23. Employee class hierarchy driver program.
(This item is displayed on pages 724 - 727 in the print version)

 1  // Fig. 13.23: fig13_23.cpp
 2  // Processing Employee derived-class objects individually
 3  // and polymorphically using dynamic binding.
 4  #include <iostream>
 5  using std::cout;
 6  using std::endl;
 7  using std::fixed;
 8
 9  #include <iomanip>
10  using std::setprecision;
11
12  #include <vector>
13  using std::vector;
14
15  // include definitions of classes in Employee hierarchy
16  #include "Employee.h"
17  #include "SalariedEmployee.h"
18  #include "HourlyEmployee.h"
19  #include "CommissionEmployee.h"
20  #include "BasePlusCommissionEmployee.h"
21
22  void virtualViaPointer( const Employee * const ); // prototype
23  void virtualViaReference( const Employee & ); // prototype
24
25  int main()
26  {
27     // set floating-point output formatting
28     cout << fixed << setprecision( 2 );
29
30     // create derived-class objects
31     SalariedEmployee salariedEmployee(
32        "John", "Smith", "111-11-1111", 800 );
33     HourlyEmployee hourlyEmployee(
34        "Karen", "Price", "222-22-2222", 16.75, 40 );
35     CommissionEmployee commissionEmployee(
36        "Sue", "Jones", "333-33-3333", 10000, .06 );
37     BasePlusCommissionEmployee basePlusCommissionEmployee(
38        "Bob", "Lewis", "444-44-4444", 5000, .04, 300 );
39
40     cout << "Employees processed individually using static binding:\n\n";
41
42     // output each Employee's information and earnings using static binding
43     salariedEmployee.print();
44     cout << "\nearned $" << salariedEmployee.earnings() << "\n\n";
45     hourlyEmployee.print();
46     cout << "\nearned $" << hourlyEmployee.earnings() << "\n\n";
47     commissionEmployee.print();
48     cout << "\nearned $" << commissionEmployee.earnings() << "\n\n";
49     basePlusCommissionEmployee.print();
50     cout << "\nearned $" << basePlusCommissionEmployee.earnings()
51        << "\n\n";
52
53     // create vector of four base-class pointers
54     vector < Employee * > employees( 4 );       
55
56     // initialize vector with Employees          
57     employees[ 0 ] = &salariedEmployee;          
58     employees[ 1 ] = &hourlyEmployee;            
59     employees[ 2 ] = &commissionEmployee;        
60     employees[ 3 ] = &basePlusCommissionEmployee;
61
62     cout << "Employees processed polymorphically via dynamic binding:\n\n";
63
64     // call virtualViaPointer to print each Employee's information
65     // and earnings using dynamic binding
66     cout << "Virtual function calls made off base-class pointers:\n\n";
67
68     for ( size_t i = 0; i < employees.size(); i++ )
69        virtualViaPointer( employees[ i ] );        
70
71     // call virtualViaReference to print each Employee's information
72     // and earnings using dynamic binding
73     cout << "Virtual function calls made off base-class references:\n\n";
74
75     for ( size_t i = 0; i < employees.size(); i++ )                 
76        virtualViaReference( *employees[ i ] ); // note dereferencing
77
78     return 0;
79  } // end main
80
81  // call Employee virtual functions print and earnings off a   
82  // base-class pointer using dynamic binding                   
83  void virtualViaPointer( const Employee * const baseClassPtr ) 
84  {                                                             
85     baseClassPtr->print();                                     
86     cout << "\nearned $" << baseClassPtr->earnings() << "\n\n";
87  } // end function virtualViaPointer                           
88
89  // call Employee virtual functions print and earnings off a  
90  // base-class reference using dynamic binding                
91  void virtualViaReference( const Employee &baseClassRef )     
92  {                                                            
93     baseClassRef.print();                                     '
94     cout << "\nearned $" << baseClassRef.earnings() << "\n\n";
95  } // end function virtualViaReference                        

 Employees processed individually using static binding:

 salaried employee: John Smith
 social security number: 111-11-1111
 weekly salary: 800.00
 earned $800.00

 hourly employee: Karen Price
 social security number: 222-22-2222
 hourly wage: 16.75; hours worked: 40.00
 earned $670.00

 commission employee: Sue Jones
 social security number: 333-33-3333
 gross sales: 10000.00; commission rate: 0.06
 earned $600.00

 base-salaried commission employee: Bob Lewis
 social security number: 444-44-4444
 gross sales: 5000.00; commission rate: 0.04; base salary: 300.00
 earned $500.00

 Employees processed polymorphically using dynamic binding:

 Virtual function calls made off base-class pointers:

 salaried employee: John Smith
 social security number: 111-11-1111
 weekly salary: 800.00
 earned $800.00

 hourly employee: Karen Price
 social security number: 222-22-2222
 hourly wage: 16.75; hours worked: 40.00
 earned $670.00

 commission employee: Sue Jones
 social security number: 333-33-3333
 gross sales: 10000.00; commission rate: 0.06
 earned $600.00

 base-salaried commission employee: Bob Lewis
 social security number: 444-44-4444
 gross sales: 5000.00; commission rate: 0.04; base salary: 300.00
 earned $500.00

 Virtual function calls made off base-class references:

 salaried employee: John Smith
 social security number: 111-11-1111
 weekly salary: 800.00
 earned $800.00

 hourly employee: Karen Price
 social security number: 222-22-2222
 hourly wage: 16.75; hours worked: 40.00
 earned $670.00

 commission employee: Sue Jones
 social security number: 333-33-3333
 gross sales: 10000.00; commission rate: 0.06
 earned $600.00

 base-salaried commission employee: Bob Lewis
 social security number: 444-44-4444
 gross sales: 5000.00; commission rate: 0.04; base salary: 300.00
 earned $500.00


Line 54 allocates vector employees, which contains four Employee pointers. Line 57 aims employees[0] at object salariedEmployee. Line 58 aims employees[1] at object hourlyEmployee. Line 59 aims employees[2] at object commissionEmployee. Line 60 aims employee[3] at object basePlusCommissionEmployee. The compiler allows these assignments, because a SalariedEmployee is an Employee, an HourlyEmployee is an Employee, a CommissionEmployee is an Employee and a BasePlusCommissionEmployee is an Employee. Therefore, we can assign the addresses of SalariedEmployee, HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee objects to base-class Employee pointers (even though Employee is an abstract class).

The for statement at lines 6869 traverses vector employees and invokes function virtualViaPointer (lines 8387) for each element in employees. Function virtualViaPointer receives in parameter baseClassPtr (of type const Employee * const) the address stored in an employees element. Each call to virtualViaPointer uses baseClassPtr to invoke virtual functions print (line 85) and earnings (line 86). Note that function virtualViaPointer does not contain any SalariedEmployee, HourlyEmployee, CommissionEmployee or BasePlusCommissionEmployee type information. The function knows only about base-class type Employee. Therefore, at compile time, the compiler cannot know which concrete class's functions to call through baseClassPtr. Yet at execution time, each virtual-function invocation calls the function on the object to which baseClassPtr points at that time. The output illustrates that the appropriate functions for each class are indeed invoked and that each object's proper information is displayed. For instance, the weekly salary is displayed for the SalariedEmployee, and the gross sales are displayed for the CommissionEmployee and BasePlusCommissionEmployee. Also note that obtaining the earnings of each Employee polymorphically in line 86 produces the same results as obtaining these employees' earnings via static binding in lines 44, 46, 48 and 50. All virtual function calls to print and earnings are resolved at runtime with dynamic binding.

Finally, another for statement (lines 7576) traverses employees and invokes function virtualViaReference (lines 9195) for each element in the vector. Function virtualViaReference receives in its parameter baseClassRef (of type const Employee &) a reference formed by dereferencing the pointer stored in each employees element (line 76). Each call to virtualViaReference invokes virtual functions print (line 93) and earnings (line 94) via reference baseClassRef to demonstrate that polymorphic processing occurs with base-class references as well. Each virtual-function invocation calls the function on the object to which baseClassRef refers at runtime. This is another example of dynamic binding. The output produced using base-class references is identical to the output produced using base-class pointers.


[Page 728]

Previous Page
Next Page