12.7 Case Study: Payroll System Using Polymorphism

This section reexamines the CommissionEmployeeBasePlusCommissionEmployee hierarchy that we explored throughout Section 11.3. 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:

A company pays its employees weekly. The employees are of three types: Salaried employees are paid a fixed weekly salary regardless of the number of hours worked, 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 and CommissionEmployee. Class BasePlusCommissionEmployee derives from CommissionEmployee and represents the last employee type. The UML class diagram in Fig. 12.7 shows the inheritance hierarchy for our polymorphic employee payroll application. The abstract class name Employee is italicized, per the convention of the UML.

Abstract base class Employee declares the “interface” to the hierarchy—that 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

Fig. 12.7 Employee hierarchy UML class diagram.

social security number, so private data members firstName, lastName and socialSecurityNumber appear in abstract base class Employee.

Software Engineering Observation 12.10

A derived class can inherit interface and/or implementation from a base class. Hierarchies designed for implementation inheritance tend to have their functionality high in the hierarchy—each derived class inherits one or more member functions from 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 hierarchy—a base class specifies one or more functions that should be defined by every derived class, but the individual derived classes provide their own implementations of the function(s).

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

12.7.1 Creating Abstract Base Class Employee

Class Employee (Figs. 12.912.10, discussed in further detail shortly) provides functions earnings and toString, in addition to various get and set functions that manipulate Employee’s data members. An earnings function certainly applies generally 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 function—there’s 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.

The test program maintains 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 class—with inheritance, however, all objects of all concrete 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.

Function toString in class Employee returns a string containing the first name, last name and social security number of the employee. As we’ll see, each derived class of Employee overrides function toString to output the employee’s type (e.g., "salaried employee:") followed by the rest of the employee’s information. Each each derived class’s toString could also call earnings, even though earnings is a pure-virtual function in base class Employee, because each concrete class is guaranteed to have an implementation of earnings. For this reason, even class Employee’s toString function can call earnings— at runtime, when you call toString through an Employee pointer or reference, you’re always calling it on an object of a concrete derived-class.

The diagram in Fig. 12.8 shows each of the four classes in the hierarchy down the left side and functions earnings and toString across the top. For each class, the diagram shows the desired results of each function. Italic text represents where the values from a particular object are used in the earnings and toString functions. Class Employee specifies “=0” for function earnings to indicate that it’s a pure virtual function and hence has no implementation. 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’re not overridden in any of the derived classes—each of these functions is inherited and used “as is” by each of the derived classes.

Fig. 12.8 Polymorphic interface for the Employee hierarchy classes.

Employee Class Header

Let’s consider class Employee’s header (Fig. 12.9). The public member functions include a constructor that takes the first name, last name and social security number as arguments (line 10); a C++11 default virtual destructor (line 11) that the compiler generates; set functions that set the first name, last name and social security number (lines 13, 16 and 19, respectively); get functions that return the first name, last name and social security number (lines 14, 17 and 20, respectively); pure virtual function earnings (line 23) and virtual function toString (line 24).

Fig. 12.9 Employee abstract base class.

Alternate View

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

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

Employee Class Member-Function Definitions

Figure 12.10 contains the member-function definitions for class Employee. No implementation is provided for virtual function earnings. The Employee constructor (lines 9–11) does not validate the social security number. Normally, such validation should be provided.

Fig. 12.10 Employee class implementation file.

Alternate View

 1   // Fig. 12.10: Employee.cpp
 2   // Abstract-base-class Employee member-function definitions.
 3   // Note: No definitions are given for pure virtual functions.
 4   #include <sstream>
 5   #include "Employee.h" // Employee class definition
 6   using namespace std;
 7
 8   // constructor
 9   Employee::Employee(const string& first, const string& last,
10      const string& ssn)
11      : firstName(first), lastName(last), socialSecurityNumber(ssn) {}
12
13   // set first name
14   void Employee::setFirstName(const string& first) {firstName = first;}
15
16   // return first name
17   string Employee::getFirstName() const {return firstName;}
18
19   // set last name
20   void Employee::setLastName(const string& last) {lastName = last;}
21
22   // return last name
23   string Employee::getLastName() const {return lastName;}
24
25   // set social security number
26   void Employee::setSocialSecurityNumber(const string& ssn) {
27      socialSecurityNumber = ssn; // should validate
28   }
29
30   // return social security number
31   string Employee::getSocialSecurityNumber() const {
32      return socialSecurityNumber;
33   }
34
35   // toString Employee's information (virtual, but not pure virtual)
36   string Employee::toString() const {
37      return getFirstName() + " "s + getLastName() +
38         "
social security number: "s + getSocialSecurityNumber();
39   }

The virtual function toString (lines 36–39) provides an implementation that will be overridden in each of the derived classes. Each of these functions will, however, use the Employee class’s version of toString to get a string containing the information common to all classes in the Employee hierarchy.

12.7.2 Creating Concrete Derived Class SalariedEmployee

Class SalariedEmployee (Figs. 12.1112.12) derives from class Employee (line 9 of Fig. 12.11). 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 11–12); a C++11 default virtual destructor (line 13); a set function to assign a new nonnegative value to data member weeklySalary (line 15); a get function to return weeklySalary’s value (line 16); a virtual function earnings that calculates a SalariedEmployee’s earnings (line 19) and a virtual function toString (line 20) that outputs the employee’s type, namely, "salaried employee: " followed by employee-specific information produced by base class Employee’s toString function and SalariedEmployee’s getWeeklySalary function.

Fig. 12.11 SalariedEmployee class header.

Alternate View

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

SalariedEmployee Class Member-Function Definitions

Figure 12.12 contains the member-function definitions for SalariedEmployee. The class’s constructor passes the first name, last name and social security number to the Employee constructor (line 12) to initialize the private data members that are inherited from the base class, but not directly accessible in the derived class. Function earnings (line 30) overrides pure virtual function earnings in Employee to provide a concrete implementation that returns the SalariedEmployee’s weekly salary. If we did not define earnings, class SalariedEmployee would be an abstract class, and attempting to instantiate a SalariedEmployee object would cause a compilation error. In class SalariedEmployee’s header, we declared member functions earnings and toString as virtual (lines 19–20 of Fig. 12.11)—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 all the way down the class hierarchy. Explicitly declaring such functions virtual at every level of the hierarchy promotes program clarity. Not declaring earnings as pure virtual signals our intent to provide an implementation in this concrete class.

Fig. 12.12 SalariedEmployee class implementation file.

Alternate View

 1   // Fig. 12.12: SalariedEmployee.cpp
 2   // SalariedEmployee class member-function definitions.
 3   #include <iomanip>
 4   #include <stdexcept>
 5   #include <sstream>
 6   #include "SalariedEmployee.h" // SalariedEmployee class definition
 7   using namespace std;
 8
 9   // constructor
10   SalariedEmployee::SalariedEmployee(const string& first,
11      const string& last, const string& ssn, double salary)
12      :  Employee(first, last, ssn) {
13      setWeeklySalary(salary);
14   }
15
16   // set salary
17   void SalariedEmployee::setWeeklySalary(double salary) {
18      if (salary < 0.0) {
19         throw invalid_argument("Weekly salary must be >= 0.0");
20      }
21
22      weeklySalary = salary;
23   }
24
25   // return salary
26   double SalariedEmployee::getWeeklySalary() const {return weeklySalary;}
27
28   // calculate earnings;
29   // override pure virtual function earnings in Employee
30   double SalariedEmployee::earnings() const {return getWeeklySalary();}
31
32   // return a string representation of SalariedEmployee's information
33   string SalariedEmployee::toString() const {
34      ostringstream output;
35      output << fixed << setprecision(2);
36      output << "salaried employee: "
37         << Employee::toString() // reuse abstract base-class function
38         << "
weekly salary: " << getWeeklySalary();
39      return output.str();
40   }

Function toString of class SalariedEmployee (lines 33–40 of Fig. 12.12) overrides Employee function toString. If class SalariedEmployee did not override toString, SalariedEmployee would inherit the Employee version of toString. In that case, SalariedEmployee’s toString function would simply return the employee’s full name and social security number, which does not adequately represent a SalariedEmployee. To create a string representation of a SalariedEmployee’s complete information, the derived class’s toString function returns "salaried employee: " followed by the base-class Employee-specific information (i.e., first name, last name and social security number) returned by invoking the base class’s toString function using the scope resolution operator (line 37). Without Employee::, the toString call would cause infinite recursion. The string produced by SalariedEmployee’s toString function also contains the employee’s weekly salary obtained by invoking the class’s getWeeklySalary function.

12.7.3 Creating Concrete Derived Class CommissionEmployee

The CommissionEmployee class (Figs. 12.1312.14) derives from Employee (Fig. 12.13, line 9). The member-function implementations in Fig. 12.14 include a constructor (lines 10–15) that takes a first name, last name, social security number, sales amount and commission rate; set functions (lines 18–24 and 30–36) to assign new values to data members grossSales and commissionRate, respectively; get functions (lines 27 and 39–41) that retrieve their values; function earnings (lines 44–46) to calculate a CommissionEmployee’s earnings; and function toString (lines 49–56) to output the employee’s type, namely, "commission employee: " and employee-specific information. The constructor passes the first name, last name and social security number to the Employee constructor (line 12) to initialize Employee’s private data members. Function toString calls base-class function toString (line 52) to get a string representation of the Employee-specific information.

Fig. 12.13 CommissionEmployee class header.

Alternate View

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

Fig. 12.14 CommissionEmployee class implementation file.

Alternate View

 1   // Fig. 12.14: CommissionEmployee.cpp
 2   // CommissionEmployee class member-function definitions.
 3   #include <iomanip>
 4   #include <stdexcept>
 5   #include <sstream>
 6   #include "CommissionEmployee.h" // CommissionEmployee class definition
 7   using namespace std;
 8
 9   // constructor
10   CommissionEmployee::CommissionEmployee(const string &first,
11      const string &last, const string &ssn, double sales, double rate)
12      : Employee(first, last, ssn) {
13      setGrossSales(sales);
14      setCommissionRate(rate);
15   }
16
17   // set gross sales amount
18   void CommissionEmployee::setGrossSales(double sales) {
19      if (sales < 0.0) {
20         throw invalid_argument("Gross sales must be >= 0.0");
21      }
22
23      grossSales = sales;
24   }
25
26   // return gross sales amount
27   double CommissionEmployee::getGrossSales() const {return grossSales;}
28
29   // set commission rate
30   void CommissionEmployee::setCommissionRate(double rate) {
31      if (rate <= 0.0 || rate > 1.0) {
32      throw invalid_argument("Commission rate must be > 0.0 and < 1.0");
33      }
34
35      commissionRate = rate;
36   }
37
38   // return commission rate
39   double CommissionEmployee::getCommissionRate() const {
40      return commissionRate;
41   }
42
43   // calculate earnings; override pure virtual function earnings in Employee
44   double CommissionEmployee::earnings() const {
45      return getCommissionRate() * getGrossSales();
46   }
47
48   // return a string representation of CommissionEmployee's information
49   string CommissionEmployee::toString() const {
50      ostringstream output;
51      output << fixed << setprecision(2);
52      output << "commission employee: " << Employee::toString()
53         << "
gross sales: " << getGrossSales()
54         << "; commission rate: " << getCommissionRate();
55      return output.str();
56   }

12.7.4 Creating Indirect Concrete Derived Class BasePlusCommissionEmployee

Class BasePlusCommissionEmployee (Figs. 12.1512.16) directly inherits from class CommissionEmployee (line 9 of Fig. 12.15) and therefore is an indirect derived class of class Employee. Class BasePlusCommissionEmployee’s member-function implementations in Fig. 12.16 include a constructor (lines 10–15) 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 18–24) to assign a new value to data member baseSalary and a get function (lines 27–29) to return baseSalary’s value. Function earnings (lines 33–35) calculates a BasePlusCommissionEmployee’s earnings. Line 34 in function earnings calls base class CommissionEmployee’s earnings function to calculate the commission-based portion of the employee’s earnings. BasePlusCommissionEmployee’s toString function (lines 38–44) returns "base-salaried", followed by the result of base-class CommissionEmployee’s toString function, then the base salary. The resulting string begins with "base-salaried commission employee: " followed by the rest of the BasePlusCommissionEmployee’s information. Recall that CommissionEmployee’s toString gets a string containing the employee’s first name, last name and social security number by invoking the toString function of its base class (i.e., Employee). BasePlusCommissionEmployee’s toString initiates a chain of functions calls that spans all three levels of the Employee hierarchy.

Fig. 12.15 BasePlusCommissionEmployee class header.

Alternate View

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

Fig. 12.16 BasePlusCommissionEmployee class implementation file.

Alternate View

 1   // Fig. 12.16: BasePlusCommissionEmployee.cpp
 2   // BasePlusCommissionEmployee member-function definitions.
 3   #include <iomanip>
 4   #include <stdexcept>
 5   #include <sstream>
 6   #include "BasePlusCommissionEmployee.h"
 7   using namespace std;
 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      setBaseSalary(salary); // validate and store base salary
15   }
16
17   // set base salary
18   void BasePlusCommissionEmployee::setBaseSalary(double salary) {
19      if (salary < 0.0) {
20         throw invalid_argument("Salary must be >= 0.0");
 21      }
22
23      baseSalary = salary;
24   }
25
26   // return base salary
27   double BasePlusCommissionEmployee::getBaseSalary() const {
28      return baseSalary;
29   }
30
31   // calculate earnings;
32   // override virtual function earnings in CommissionEmployee
33   double BasePlusCommissionEmployee::earnings() const {
34      return getBaseSalary() + CommissionEmployee::earnings();
35   }
36
37   // return a string representation of a BasePlusCommissionEmployee
38   string BasePlusCommissionEmployee::toString() const {
39      ostringstream output;
40      output << fixed << setprecision(2);
41      output << "base-salaried " << CommissionEmployee::toString()
42         << "; base salary: " << getBaseSalary();
43   return output.str();
44   }

12.7.5 Demonstrating Polymorphic Processing

To test our Employee hierarchy, the program in Fig. 12.17 creates an object of each of the three concrete classes SalariedEmployee, CommissionEmployee and BasePlusCommissionEmployee. The program manipulates these objects, first with static binding, then polymorphically, using a vector of Employee base-class pointers. Lines 20–25 create objects of each of the three concrete Employee derived classes. Lines 28–34 output each Employee’s information and earnings. Each member-function invocation in lines 29–34 is an example of static binding—at 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 toString and earnings functions are called.



Fig. 12.17 Processing Employee derived-class objects with static binding then polymorphically using dynamic binding.

Alternate View

 1   // Fig. 12.17: fig12_17.cpp
 2   // Processing Employee derived-class objects with static binding
 3   // then polymorphically using dynamic binding.
 4   #include <iostream>
 5   #include <iomanip>
 6   #include <vector>
 7   #include "Employee.h"
 8   #include "SalariedEmployee.h"
 9   #include "CommissionEmployee.h"
10   #include "BasePlusCommissionEmployee.h"
11   using namespace std;
12
13   void virtualViaPointer(const Employee* const); // prototype
14   void virtualViaReference(const Employee&);// prototype
15
16   int main() {
17      cout << fixed << setprecision(2); // set floating-point formatting 
18
19      // create derived-class objects 
20      SalariedEmployee salariedEmployee{
21         "John", "Smith", "111-11-1111", 800};
22      CommissionEmployee commissionEmployee{
23         "Sue", "Jones", "333-33-3333", 10000, .06};
24      BasePlusCommissionEmployee basePlusCommissionEmployee{
25         "Bob", "Lewis", "444-44-4444", 5000, .04, 300};
26
27      // output each Employee’s information and earnings using static binding
28      cout << "EMPLOYEES PROCESSED INDIVIDUALLY USING STATIC BINDING
"
29         << salariedEmployee.toString()
30         << "
earned $" << salariedEmployee.earnings() << "

" 
31         << commissionEmployee.toString()
32         << "
earned $" << commissionEmployee.earnings() << "

"
33         << basePlusCommissionEmployee.toString()
34         << "
earned $" << basePlusCommissionEmployee.toString() << "

";
35
36      // create and initialize vector of three base-class pointers
37      vector<Employee *> employees{&salariedEmployee, &commissionEmployee,
38         &basePlusCommissionEmployee};                                    
39
40      cout << "EMPLOYEES PROCESSED POLYMORPHICALLY VIA DYNAMIC BINDING

";
41
42      // call virtualViaPointer to print each Employee's information
43      // and earnings using dynamic binding
44      cout << "VIRTUAL FUNCTION CALLS MADE OFF BASE-CLASS POINTERS
";
45
46      for (const Employee* employeePtr : employees) {
47         virtualViaPointer(employeePtr);
48      }
49
50      // call virtualViaReference to print each Employee's information
51      // and earnings using dynamic binding
52      cout << "VIRTUAL FUNCTION CALLS MADE OFF BASE-CLASS REFERENCES
";
53
54      for (const Employee* employeePtr : employees) {
55         virtualViaReference(*employeePtr); // note dereferencing
56      }
57   }
58
59   // call Employee virtual functions toString and earnings off a 
60   // base-class pointer using dynamic binding 
61   void virtualViaPointer(const Employee* const baseClassPtr) {
62      cout << baseClassPtr->toString()
63         << "
earned $" << baseClassPtr->earnings() << "

";
64   }
65
66   // call Employee virtual functions toString and earnings off a 
67   // base-class reference using dynamic binding 
68   void virtualViaReference(const Employee& baseClassRef) {
69      cout << baseClassRef.toString()
70         << "
earned $" << baseClassRef.earnings() << "

";
71   }

EMPLOYEES PROCESSED INDIVIDUALLY USING STATIC BINDING
salaried employee: John Smith
social security number: 111-11-1111
weekly salary: 800.00
earned $800.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 VIA 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

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

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

Lines 37–38 create and initialize the vector employees, which contains three Employee pointers that are aimed at the objects salariedEmployee, commissionEmployee and BasePlusCommissionEmployee, respectively. The compiler allows the elements to be initialized with the addresses of these objects, because a SalariedEmployee is an Employee, a CommissionEmployee is an Employee and a BasePlusCommissionEmployee is an Employee. So, we can assign the addresses of SalariedEmployee, CommissionEmployee and BasePlusCommissionEmployee objects to base-class Employee pointers, even though Employee is an abstract class.

Lines 46–48 traverse vector employees and invoke function virtualViaPointer (lines 61–64) for each element in employees. Function virtualViaPointer receives in parameter baseClassPtr the address stored in an employees element. Each call to virtualViaPointer uses baseClassPtr to invoke virtual functions toString (line 62) and earnings (line 63). Function virtualViaPointer does not contain any SalariedEmployee, CommissionEmployee or BasePlusCommissionEmployee type information. The function knows only about base-class type Employee. Therefore, the compiler cannot know which concrete class’s functions to call through baseClassPtr. Yet at execution time, each virtual-function invocation correctly calls the function on the object to which baseClassPtr currently points. 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, obtaining the earnings of each Employee polymorphically in line 63 produces the same results as obtaining these employees’ earnings via static binding in lines 30, 32 and 34. All virtual function calls to toString and earnings are resolved at runtime with dynamic binding.

Lines 54–56 traverse employees and invoke function virtualViaReference (lines 68–71) for each vector element. Function virtualViaReference receives in its parameter baseClassRef (of type const Employee&) a reference to the object obtained by dereferencing the pointer stored in each employees element (line 55). Each call to virtualViaReference invokes virtual functions toString (line 69) and earnings (line 70) via 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 and via static binding earlier in the program.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset