Case Study: Payroll System Using Polymorphism and Run-Time Type Information with Downcasting, dynamic_cast, typeid and type_info

Case Study Payroll System Using Polymorphism and Run Time Type Information with Downcasting, dynamic_cast, typeid and type_info

Recall from the problem statement at the beginning of Section 13.6 that, for the current pay period, our fictitious company has decided to reward BasePlusCommissionEmployees by adding 10 percent to their base salaries. When processing Employee objects polymorphically in Section 13.6.6, we did not need to worry about the "specifics." Now, however, to adjust the base salaries of BasePlusCommissionEmployees, we have to determine the specific type of each Employee object at execution time, then act appropriately. This section demonstrates the powerful capabilities of run-time type information (RTTI) and dynamic casting, which enable a program to determine the type of an object at execution time and act on that object accordingly.

Some compilers, such as Microsoft Visual C++ .NET, require that RTTI be enabled before it can be used in a program. Consult your compiler's documentation to determine whether your compiler has similar requirements. To enable RTTI in Visual C++ .NET, select the Project menu and then select the properties option for the current project. In the Property Pages dialog box that appears, select Configuration Properties > C/C++ > Language. Then choose Yes (/GR) from the combo box next to Enable Run-Time Type Info. Finally, click OK to save the settings.

The program in Fig. 13.25 uses the Employee hierarchy developed in Section 13.6 and increases by 10 percent the base salary of each BasePlusCommissionEmployee. Line 31 declares four-element vector employees that stores pointers to Employee objects. Lines 3441 populate the vector with the addresses of dynamically allocated objects of classes SalariedEmployee (Figs. 13.1513.16), HourlyEmployee (Figs. 13.1713.18), CommissionEmployee (Figs. 13.1913.20) and BasePlusCommissionEmployee (Figs. 13.2113.22).


Figure 13.25. Demonstrating downcasting and run-time type information.

(This item is displayed on pages 732 - 734 in the print version)

 1 // Fig. 13.25: fig13_25.cpp
 2 // Demonstrating downcasting and run-time type information.
 3 // NOTE: For this example to run in Visual C++ .NET,
 4 // you need to enable RTTI (Run-Time Type Info) for the project.
 5 #include 
 6 using std::cout;
 7 using std::endl;
 8 using std::fixed;
 9
10 #include 
11 using std::setprecision;
12
13 #include 
14 using std::vector;
15
16 #include 
17
18 // include definitions of classes in Employee hierarchy
19 #include "Employee.h"
20 #include "SalariedEmployee.h"
21 #include "HourlyEmployee.h"
22 #include "CommissionEmployee.h"
23 #include "BasePlusCommissionEmployee.h"
24
25 int main()
26 {
27 // set floating-point output formatting
28 cout << fixed << setprecision( 2 );
29
30 // create vector of four base-class pointers
31 vector < Employee * > employees( 4 );
32
33 // initialize vector with various kinds of Employees
34 employees[ 0 ] = new SalariedEmployee( 
35  "John", "Smith", "111-11-1111", 800 ); 
36 employees[ 1 ] = new HourlyEmployee( 
37  "Karen", "Price", "222-22-2222", 16.75, 40 ); 
38 employees[ 2 ] = new CommissionEmployee( 
39  "Sue", "Jones", "333-33-3333", 10000, .06 ); 
40 employees[ 3 ] = new BasePlusCommissionEmployee( 
41  "Bob", "Lewis", "444-44-4444", 5000, .04, 300 ); 
42
43 // polymorphically process each element in vector employees
44 for ( size_t i = 0; i < employees.size(); i++ )
45 {
46 employees[ i ]->print(); // output employee information
47 cout << endl;
48
49 // downcast pointer 
50 BasePlusCommissionEmployee *derivedPtr = 
51  dynamic_cast < BasePlusCommissionEmployee * >
52  ( employees[ i ] ); 
53
54 // determine whether element points to base-salaried
55 // commission employee
56 if ( derivedPtr !=0 ) // 0 if not a BasePlusCommissionEmployee
57 {
58 double oldBaseSalary = derivedPtr->getBaseSalary();
59 cout << "old base salary: $" << oldBaseSalary << endl;
60 derivedPtr->setBaseSalary( 1.10 * oldBaseSalary );
61 cout << "new base salary with 10% increase is: $"
62 << derivedPtr->getBaseSalary() << endl;
63 } // end if
64
65 cout << "earned $" << employees[ i ]->earnings() << "

";
66 } // end for
67
68 // release objects pointed to by vector's elements
69 for ( size_t j = 0; j < employees.size(); j++ )
70 {
71 // output class name 
72 cout << "deleting object of " 
73  << typeid( *employees[ j ] ).name() << endl;
74
75 delete employees[ j ];
76 } // end for
77
78 return 0;
79 } // end main
 
 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
 old base salary: $300.00
 new base salary with 10% increase is: $330.00
 earned $530.00

 deleting object of class SalariedEmployee
 deleting object of class HourlyEmployee
 deleting object of class CommissionEmployee
 deleting object of class BasePlusCommissionEmployee
 

The for statement at lines 4466 iterates through the employees vector and displays each Employee's information by invoking member function print (line 46). Recall that because print is declared virtual in base class Employee, the system invokes the appropriate derived-class object's print function.

In this example, as we encounter BasePlusCommissionEmployee objects, we wish to increase their base salary by 10 percent. Since we process the employees generically (i.e., polymorphically), we cannot (with the techniques we've learned) be certain as to which type of Employee is being manipulated at any given time. This creates a problem, because BasePlusCommissionEmployee employees must be identified when we encounter them so they can receive the 10 percent salary increase. To accomplish this, we use operator dynamic_cast (line 51) to determine whether the type of each object is BasePlusCommissionEmployee. This is the downcast operation we referred to in Section 13.3.3. Lines 5052 dynamically downcast employees[i] from type Employee * to type BasePlusCommissionEmployee *. If the vector element points to an object that is a BasePlusCommissionEmployee object, then that object's address is assigned to commissionPtr; otherwise, 0 is assigned to derived-class pointer derivedPtr.

If the value returned by the dynamic_cast operator in lines 5052 is not 0, the object is the correct type and the if statement (lines 5663) performs the special processing required for the BasePlusCommissionEmployee object. Lines 58, 60 and 62 invoke BasePlusCommissionEmployee functions getBaseSalary and setBaseSalary to retrieve and update the employee's salary.


Line 65 invokes member function earnings on the object to which employees[i] points. Recall that earnings is declared virtual in the base class, so the program invokes the derived-class object's earnings functionanother example of dynamic binding.

The for loop at lines 6976 displays each employee's object type and uses the delete operator to deallocate the dynamic memory to which each vector element points. Operator typeid (line 73) returns a reference to an object of class type_info that contains the information about the type of its operand, including the name of that type. When invoked, type_info member function name (line 73) returns a pointer-based string that contains the type name (e.g., "class BasePlusCommissionEmployee") of the argument passed to typeid. [Note: The exact contents of the string returned by type_info member function name may vary by compiler.] To use typeid, the program must include header file (line 16).

Note that we avoid several compilation errors in this example by downcasting an Employee pointer to a BasePlusCommissionEmployee pointer (lines 5052). If we remove the dynamic_cast from line 51 and attempt to assign the current Employee pointer directly to BasePlusCommissionEmployee pointer commissionPtr, we will receive a compilation error. C++ does not allow a program to assign a base-class pointer to a derived-class pointer because the is-a relationship does not applya CommissionEmployee is not a BasePlusCommissionEmployee. The is-a relationship applies only between the derived class and its base classes, not vice versa.

Similarly, if lines 58, 60 and 62 used the current base-class pointer from employees, rather than derived-class pointer commissionPtr, to invoke derived-class-only functions getBaseSalary and setBaseSalary, we would receive a compilation error at each of these lines. As you learned in Section 13.3.3, attempting to invoke derived-class-only functions through a base-class pointer is not allowed. Although lines 58, 60 and 62 execute only if commissionPtr is not 0 (i.e., if the cast can be performed), we cannot attempt to invoke derived class BasePlusCommissionEmployee functions getBaseSalary and setBaseSalary on the base class Employee pointer. Recall that, using a base class Employee pointer, we can invoke only functions found in base class Employeeearnings, print and Employee's get and set functions.





C++ How to Program
C++ How to Program (5th Edition)
ISBN: 0131857576
EAN: 2147483647
Year: 2004
Pages: 627
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net