10.6 实例研究:利用多态性的工资单系统
下面的范例程序用虚函数和多态性根据雇员的类型完成工资单的计算(见图 10.1)。所用的基类是雇员类 Employee,其派生类包括:老板类Boss,不管工作多长时间他总是有固定的周薪;销售员类 CommissionWorker,他的收入是一小部分基本工资加上销售额的一定的百分比;计件工类 PieceworkWorker,他的收入取决他生产的工件数量;小时工类HourlyWorker,他的收入以小时计算,再加上加班费。
函数earnings的调用当然要普遍适用于所有的雇员。每人收入的计算方法取决于它属于哪一类雇员。因为这些类都是由基类 Employee 派生出来的,所以函数earnings在基类Employee中被声明为virtual,并在每个派生类中都正确地实现earnings。
为计算任何雇员的收入,程序简单地使用了一个指向该雇员对象的基类指针并调用函数earnings。在一个实际的工资单系统中,各种雇员对象可能保存在一个数组(链表)中,数组每个指针都是 Employee * 类型,然后程序遍历链表中的每一个节点,并在每一个节点处用Employee * 指针调用对象的 earnings 函数。
下面看一看类Employee。该类的public成员函数包括:构造函数,该构造函数有两个参数,第一个参数是雇员的姓,第二个参数是雇员的名;析构函数,用来释放动态分配的内存;两个“get”函数,分别返回雇员的姓和名;纯虚函数earnings和虚函数print。为什么要把earnings函数声明为纯虚函数呢?因为在类Employee中提供这个函数的实现是没有意义的,将它声明为纯虚函数表示要在派生类中而不是在基类中提供具体的实现。
对于具有广泛含义的雇员,我们不能计算出他的收入,而必须首先知道该雇员的类型。程序员不会试图在基类Employee中调用该纯虚函数,所有的派生类根据相应的实现为这些类重定义earnings。
类Boss是通过public继承从类Employee派生出来的,它的public成员函数包括:构造函数,构造函数有三个参数,即雇员的姓和名以及周薪,为了初始化派生类对象中基类部分的成员firstName和lastName,雇员的姓和名传递给了类Employee的构造函数;“set”函数,用来把新值赋绐private数据成员weeklySalary;虚函数earnings,用来定义如何计算Boss的工资;虚函数print,它输出雇员类型,然后调用Employee:print()输出员工姓名。
类CommissionWorker是通过public继承从类Employee派生出的,它的public成员函数包括:构造函数,构造函数有五个参数,即姓、名、基本工资、回扣及产品销售量,井将姓和名传递给了类Employee的构造函数;"set"函数,用于将新值赋给private数据成员salary、commission和quantity;
虚函数earnings,用来定义如何计算CommissionWorker的工资;虚函数print,输出雇员类型,然后调用Employs:print()输出员工姓名。
类PieceWorker是通过public继承从类Employee派生出来的,public成员函数包括:构造函数,构造函数有四个参数,即计件工的姓、名、每件产品的工资以及生产的产品数量,并将姓和名传递给了类Employee的构造函数;"set"函数,用来将新值赋给private数据成员wagePerPiece和quantity;
虚函数earnings,用来定义如何计算PieceWorker的工资;虚函数print,它输出雇员类型,然后调用 Employee:print()输出员工姓名。
类HourlyWorker是通过public继承从类Employee派生出来的,public成员函数包括: 构造函数,构造函数有四个参数,即姓、名、每小时工资及工作的时间数,并将姓、名传递给了类Employee的构造函数;“set”函数,将新值赋给private数据成员wage和hours;虚函数earnings,用来定义如何计算HourlyWorker的工资;虚函数print,输出雇员类型,然后调用Employee:print()输出员工姓名。
1 // Fig. 10.1: employ2.h
2 // Abstract base class Employee
3 #ifndef EMPLOY2_H
4 #define EMPLOY2_H
5
6 #include<iostream.h>
7
8 class Employee {
9 public:
10 Employee( const char *, const char * );
11 ~Employee(); // destructor reclaims memory
12 const char *getFirstName() const;
13 const char *getLastName() const;
14
15 // Pure virtual function makes Employee abstract base class
16 virtual double earnings() const = 0; // pure virtual
17 virtual void print() const; // virtual
18 private:
19 char *firstName;
20 char *lastName;
21 };
22
23 #endif
24 // Fig. 10.1: employ2.cpp
25 // Member function definitions for
26 // abstract base class Employee.
27 // Note: No definitions given for pure virtual functions.
28 #include<string.h>
29 #include <assert.h>
30 #include "employ2.h"
31
32 // Constructor dynamically allocates space for the
33 // first and last name and uses strcpy to copy
34 // the first and last names into the object.
35 Employee::Employee( const char *first, const char *last )
36 {
37 firstName = new char strlen( first ) + 1 ];
38 assert( firstName != 0 ); // test that new worked
39 strcpy( firstName, first );
40
41 lastName = new char strlen( last ) + 1 ] ;
42 assert( lastName != 0 ); // test that new worked
43 strcpy( lastName, last );
44 }
45
46 // Destructor deallocates dynamically allocated memory
47 Employee::~Employee()
48 {
49 delete [] firstName;
50 delete [] lastName;
51 }
52
53 // Return a pointer to the first name
54 // Const return type prevents caller from modifying private
56 // deletes dynamic storage to prevent undefined pointer.
57 const char *Employee::getFirstName() const
58 {
59 return firstName; // caller must delete memory
60 }
61
62 // Return a pointer to the last name
63 // Const return type prevents caller from modifying private
64 // data. Caller should copy returned string before destructor
65 // deletes dynamic storage to prevent undefined pointer
66 const char *Employee::getLastName() const
67 {
68 return lastName; // caller must delete memory
69 }
7O
71 // Print the name of the Employee
72 void Employee::print() const
73 { cout << firstName << ' ' << lastName; }
74 // Fig. 10.1: boss1.h
76 #ifndef BOSS1_H
78 #include "employ2.h"
79
80 class Boss : public Employee {
81 public:
82 Boss( const char *, const char *, double = 0.0 );
83 void setWeeklySalary( double );
84 virtual double earnings() const;
85 virtual void print() const;
86 private:
87 double weeklySalary;
88 };
89
90 #endif
91 // Fig. 10.1: boss1.cpp
92 // Member function definitions for class Boss
93 #include "boss1.h"
94
95 // Constructor function for class Boss
96 BOSS::BOSS( const char *first, const char *last, double s )
97 : Employee( first, last ) // call base-class constructor
98 { setWeeklySalary( s ); }
99
100 // Set the Boss's salary
101 void Boss::setWeeklySalary( double s )
102 { weeklySalary = s > 0 ? s : 0; }
103
104 // Get the BOSS'S pay
105 double Boss::earnings() const { return weeklySalary; }
106
107 // Print the BOSS'S name
108 void Boss::print() const
109 {
110 cout << "\n Boss: ";
111 Employee::print();
112 }
113 / Fig. 10.1: commisl.h
115 #ifndef COMMIS1_H
116 #define COMMIS1_H
117 #include "employ2.h"
118
119 class commissionWorker : public Employee {
120 public:
121 CommissionWorker( const char *, const char *,
122 double = 0.0, double = 0.0,
123 int= 0 );
124 void setSalary( double );
125 void setCommission( double );
126 void setQuantity( int );
127 virtual double earnings() const;
128 virtual void print() const;
129 private:
130 double salary; // base salary per week
131 double commission; // amount per item sold
132 int quantity; // total items sold for week
133 };
134
135 #endif
136 // Fig. 10.1; commis1.cpp
137 // Member function definitions for class CommissionWorker
138 #include <iostream.h>
139 #include "commis1.h"
140
141 // Constructor for class CommissionWorker
142 CommissionWorker::CommissionWorker( const char * first,
143 const char *last, double s, double c, int q )
144 : Employee( first, last ) // call base-class constructor
145 {
146 setSalary( s );
147 setCommission( c );
148 setQuantity( q );
149 }
150
151 // Set CommissionWorker's weekly base salary
152 void CommissionWorker::setSalary( double s )
153 { salary = s > 0 ? s : 0; }
154
155 // Set CommissionWorker's conunission
156 void CommissionWorker::setCommission( double c )
157 { commission = c > 0 ? c : 0; }
158
159 // Set commissionWorker's quantity sold
160 void CommissionWorker::setQuantity( int q )
161 { quantity = q > 0 ? q : 0; }
i62
163 // Determine CommissionWorker's earnings
164 double CommissionWorker::earnings() const
165 { return salary + commission * quantity; }
166
167 // Print the CommissionWorker's name
168 void CommissionWorker::print() const
169 {
170 cout << "\nCommission worker: ";
171 Employee::print();
172 }
173 // Fig. 10.1: piecel.h
174 // pieceWorker class derived from Employee
175 #ifndef PIECE1_H
176 #define PIECE1_H
177 #include "employ2.h"
178
179 class PieceWorker : public Employee {
180 public:
181 PieceWorker( const char *, const char *,
182 double = 0.0, int = 0);
183 void setWage( double );
184 void setQuantity( int );
185 virtual double earnings() const;
186 virtual void print() const;
187 private:
188 double wagePerPiece; // wage for each piece output
189 int quantity; // output for week
190 };
191
192 #endif
193 // Fig. 10.1: piecel.cpp
194 // Member function definitions for class pieceWorker
195 #include <iostream.h>
196 #include "piecel.h"
197
198 // Constructor for class PieceWorker
199 pieceWorker::pieceWorker( const char *first, const char *last,
200 double w, int q )
201 : Employee( first, last ) // call base-class constructor
202 {
203 setWage( w );
204 setQuantity( q );
205 }
206
207 // Set the wage
208 void PieceWorker::setwage( double w )
209 { wagePerPiece = w > 0 ? w : 0; }
210
211 // Set the number of items output
212 void PieceWorker::setQuantity( int q )
213 { quantity = q > 0 ? q : 0; }
214
215 // Determine the PieceWorker's earnings
216 double PieceWorker::earnings() const
217 { return quantity * wagePerPiece; }
218
219 // Print the PieceWorker's name
220 void PieceWorker::print() const
221 {
222 cout << "\n Piece worker: ";
223 Employee::print();
224 }
225 // Fig. 10.1: hourlyl.h
226 // Definition of class HourlyWorker
227 #ifndef HOURLY1_H
228 #define HOURLY1_H
229 #include "employ2.h"
230
231 class HourlyWorker : public Employee {
232 public:
233 HourlyWorker( const char *, const char *,
234 double = 0.0, double = 0.0);
235 void setWage( double );
236 void setHours( double );
237 virtual double earnings() const;
238 virtual void print () const;
239 private:
240 double wage; // wage per hour
241 double hours; // hours worked for week
242 } ;
243
244 #endif
245 // Fig. 10.1: hourly1.cpp
246 // Member function definitions for class HourlyWorker
247 #include <iostream.h>
248 #include "hourly1.h"
249
250 // Constructor for class HourlyWorker
251 HourlyWorker::HourlyWorker( const char *first,
252 const char *last,
253 double w, double h }
254 : Employee( first, last ) // call base-class constructor
255 {
256 setwage( w );
257 setHours( h );
258 }
259
260 // Set the wage
261 void HourlyWorker::setwage( double w )
262 { wage = w > 0 ? w : 0; }
263
264 // Set the hours worked
265 void HourlyWorker::setHours( double h )
266 { hours = h >= 0 && h < 168 ? h : 0; }
267
268 // Get the HourlyWorker's pay
269 double HourlyWorker::earnings() const
270 {
271 if ( hours <= 40 ) // no overtime
272 return wage * hours;
273 else // overtime is paid at wage * 1.5
274 return 40 * wage + ( hours - 4O ) * wage * 1.5;
275 }
276
277 // Print the HourlyWorker's name
279 {
280 cout << "\n Hourly worker: ";
281 Employee::print();
282 }
283 // Fig. 10.1: figl0_01.cpp
284 // Driver for Employee hierarchy
285 #include <iostream.h>
286 #include <iomanip.h>
287 #include "employ2.h
288 #include "boss1.h"
289 #include "commis1.h"
290 #include "piece1.h"
291 #include "hourly1.h"
292
293 void virtualViaPointer( const Employee * );
294 void virtualViaReference( const Employee & );
295
296 int main()
297 {
298 // set output formatting
299 cout << setiosflags( ios::fixed | ios::showpoint )
300 << setprecision( 2 );
301
302 Boss b( "John", "Smith", 800.00 );
303 b.print(); // static binding
304 cout << "earned $" << b.earnings(); // static binding
305 virtualViaPointer( &b ); // uses dynamic binding
306 virtualViaReferenee( b ); // uses dynamic binding
307
308 CommissionWorker c( "Sue", "Jones", 200.0, 3.0, 150 );
309 c.print(); // static binding
310 cout << "earned $" << c.earnings(); // static binding
311 virtualViaPointer( &c )/ // uses dynamic binding
312 virtualViaReference( c ); // uses dynamic binding
313
314 PieceWorker p( "Bob", "Lewis", 2.5, 200 );
315 p.print(); // static binding
316 cout << "earned $" << p.earnings(); // static binding
317 virtualViaPointer( &p ); // uses dynamic binding
318 virtualViaReference( p ); // uses dynamic binding
319
320 HourlyWorker h( "Karen", "Price", 18.75, 40 );
321 h.print(); // static binding
322 cout << "earned $" << h.earnings(); // static binding
323 virtualViaPointer( &h ); // uses dynamic binding
324 virtualViaReference( h ); // uses dynamic binding
325 cout << endl;
326 return 0;
327 }
328
329 // Make virtual function calls off a base-class pointer
330 // using dynamic binding.
331 void virtualViaPointer( const Employee *baseClassPtr )
332 {
333 baseClassPtr->print();
334 cout << "earned $" << baseClassPtr->earnings();
335 }
336
337 // Make virtual function calls off a base-class reference
338 // using dynamic binding.
339 void virtualViaReference( const Employee &baseClassRef )
340 {
341 baseClassRef.print();
342 cout << " earned $ " << baseclassRef.earnings();
343 }
输出结果:
BOSS: John Smith earned $800,00
Boss: John Smith earned $800.00
Boss: John Smith earned $800.00
Commission Worker: Sue Jones earned $650.00
Commission worker: Sue Jones earned $650.00
Commission worker: Sue Jones earned $650,00
Piece worker: Bob Lewis earned $500.00
Piece worker: Bob Lewis earned $500.00
Piece worker: Bob Lewis earned $500.00
Hourly worker: Karen Price earned $550.00
Hourly worker: Karen Price earned $050.00
Hourly worker: Karen Price earned $550.00
图 10.1 Employee 类层次的多态性
驱动程序 main 函数中的四小段代码是类似的,因此我们只讨论处理 Boss 对象的第一段代码。
第302行:
Boss b("John","Smith",800.OO);
实例化了类Boss的派生类对象b,并为构造函数提供了参数(即姓和名以及固定的周薪)。
第303行:
b.print(); // static binding
用圆点成员选择运算符显式地调用类Boss中的成员函数print。在编译时就可以知道被调用函数的对象类型,所以它是静态关联。使用该调用是为了和用动态关联调用函数print做一比较。
第304行:
cout << " earned $ " << b.earnings(); // static binding
用圆点成员选择运算符显式地调用类Boss中的成员函数earnings,这也是一例静态关联。使用该调用是为了和用动态关联调用函数earnings做一比较。
第305行:
virtualViaPointer(&b); // uses dynamic binding
用派生类对象b的地址调用函数virtualViaPointer(第331行)。函数在参数baseClassPtr中接收这个地址,该参数声明为constEmployee *,这正是实现多态性所必须要做的。
第333行:
baseClassPtr->print()
调用baseClassPtr所指向对象的成员函数print。由于print在基类中被声明为虚函数,因此系统调用了派生类对象的print函数(仍然是多态性行为)。该函数调用是一例动态关联,即用基类指针调用虚函数,以便在执行时才确定调用哪一个函数。
第334行:
cout<<"earned $ "<<baseClassPtr—>earnings();
调用baseClassPtr所指向对象的成员函数earnings。由于earnings在基类中被声明为虚函数,因此系统调用了派生类对象的earnings函数,这也是动态关联的一个范例。
第306行:
virtualViaReference(b); // uses dynamic binding
调用函数 vitualViaRefrence(第339行)演示多态性也可以用基类引用调用虚函数来完成。该函数在参数 baseClassRef 中接收对象b,该参数声明为constEmployee&。这就是通过引用来影响多态行为。
第341行:
baseClassRef.print();
调用 baseClassRef 所引用对象的成员函数 print。由于 print 在基类中被声明为虚函数,因此系统调用了派生类对象的print函数。该函数调用是一例动态关联,即用基类引用调用函数,以便在执行时才确定调用哪一个函数。
第342行:
cout<< "earned $ "<<baseClassRef.earnings();
调用 baseClassRef 所引用对象的成员函数 earnings。由于 earnings 在基类中被声明为虚函数,因此系统调用了派生类对象的earnings函数,这也是动态关联的一个范例。