ITEEDU

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()输出员工姓名。

 // Fig. 10.1: employ2.h
 // Abstract base class Employee
 #ifndef EMPLOY2_H
 #define EMPLOY2_H
  #include< iostream.h>
  class Employee {
 public:
    Employee( const char *, const char * );
   ~Employee();  // destructor reclaims memory
   const char *getFirstName() const;
    const char *getLastName() const;
   // Pure virtual function makes Employee abstract base class
   virtual double earnings() const = 0;  // pure virtual
   virtual void print() const;          // virtual
 private:
   char *firstName;
   char *lastName;
 };
 #endif
 // Fig. 10.1: employ2.cpp
 // Member function definitions for
 // abstract base class Employee.
 // Note: No definitions given for pure virtual functions.
 #include < string.h>
 #include < assert.h>
 #include "employ2.h"
 // Constructor dynamically allocates space for the
 // first and last name and uses strcpy to copy
 // the first and last names into the object.
 Employee::Employee( const char *first, const char *last )
 {
   firstName = new char strlen( first ) + 1 ];
   assert( firstName != 0 );   // test that new worked
   strcpy( firstName, first );
   lastName = new char strlen( last ) + 1 ] ;
   assert( lastName != 0 );    // test that new worked
   strcpy( lastName, last );
 }
 // Destructor deallocates dynamically allocated memory
 Employee::~Employee()
 {
   delete [] firstName;
   delete [] lastName;
 }
 // Return a pointer to the first name
 // Const return type prevents caller from modifying private
 // deletes dynamic storage to prevent undefined pointer.
 const char *Employee::getFirstName() const
 {
   return firstName;  // caller must delete memory
 }
 // Return a pointer to the last name
 // Const return type prevents caller from modifying private
 // data. Caller should copy returned string before destructor
 // deletes dynamic storage to prevent undefined pointer
 const char *Employee::getLastName() const
 {
    return lastName;  // caller must delete memory
 }
 // Print the name of the Employee
 void Employee::print() const
   { cout << firstName << ' ' << lastName; }
 // Fig. 10.1: boss1.h
 #ifndef BOSS1_H
 #include "employ2.h"
 class Boss : public Employee {
 public:
   Boss( const char *, const char *, double = 0.0 );
   void setWeeklySalary( double );
   virtual double earnings() const;
   virtual void print() const;
 private:
   double weeklySalary;
 };
 #endif
 // Fig. 10.1: boss1.cpp
 // Member function definitions for class Boss
 #include "boss1.h"
 // Constructor function for class Boss
 BOSS::BOSS( const char *first, const char *last, double s )
   : Employee( first, last )  // call base-class constructor
 { setWeeklySalary( s ); }
 // Set the Boss's salary
 void Boss::setWeeklySalary( double s )
   { weeklySalary = s > 0 ? s : 0; }
 // Get the BOSS'S pay
 double Boss::earnings() const  { return weeklySalary; }
 // Print the BOSS'S name
 void Boss::print() const
 {
   cout << "\n          Boss: ";
   Employee::print();
 }
 / Fig. 10.1: commisl.h
 #ifndef COMMIS1_H
 #define COMMIS1_H
 #include "employ2.h"
 class commissionWorker : public Employee {
 public:
   CommissionWorker( const char *, const char *,
                     double = 0.0, double = 0.0,
                     int= 0 );
   void setSalary( double );
   void setCommission( double );
   void setQuantity( int );
   virtual double earnings() const;
   virtual void print() const;
 private:
   double salary;      // base salary per week
   double commission;  // amount per item sold
   int quantity;      // total items sold for week
 };
 #endif
 // Fig. 10.1; commis1.cpp
 // Member function definitions for class CommissionWorker
 #include < iostream.h>
 #include "commis1.h"
 // Constructor for class CommissionWorker
 CommissionWorker::CommissionWorker( const char * first,
               const char *last, double s, double c, int q )
   : Employee( first, last )  // call base-class constructor
 {
   setSalary( s );
   setCommission( c );
   setQuantity( q );
 }
 // Set CommissionWorker's weekly base salary
 void CommissionWorker::setSalary( double s )
   { salary = s > 0 ? s : 0; }
 // Set CommissionWorker's conunission
 void CommissionWorker::setCommission( double c )
   { commission = c > 0 ? c : 0; }
 // Set commissionWorker's quantity sold
 void CommissionWorker::setQuantity( int q )
   { quantity = q > 0 ? q : 0; }
// Determine CommissionWorker's earnings
 double CommissionWorker::earnings() const
   { return salary + commission * quantity; }
 // Print the CommissionWorker's name
 void CommissionWorker::print() const
 {
   cout << "\nCommission worker: ";
   Employee::print();
 }
 // Fig. 10.1: piecel.h
 // pieceWorker class derived from Employee
 #ifndef PIECE1_H
 #define PIECE1_H
 #include "employ2.h"
 class PieceWorker : public Employee {
 public:
   PieceWorker( const char *, const char *,
   double = 0.0, int = 0);
   void setWage( double );
   void setQuantity( int );
   virtual double earnings() const;
   virtual void print() const;
 private:
   double wagePerPiece; // wage for each piece output
   int quantity;       // output for week
 };
 #endif
 // Fig. 10.1: piecel.cpp
 // Member function definitions for class pieceWorker
 #include < iostream.h>
 #include "piecel.h"
 // Constructor for class PieceWorker
 pieceWorker::pieceWorker( const char *first, const char *last,
                          double w, int q )
   : Employee( first, last )  // call base-class constructor
 {
   setWage( w );
   setQuantity( q );
 }
 // Set the wage
 void PieceWorker::setwage( double w )
   { wagePerPiece = w > 0 ? w : 0; }
 // Set the number of items output
 void PieceWorker::setQuantity( int q )
   { quantity = q > 0 ? q : 0; }
 // Determine the PieceWorker's earnings
 double PieceWorker::earnings() const
   { return quantity * wagePerPiece; }
 // Print the PieceWorker's name
 void PieceWorker::print() const
 {
   cout << "\n    Piece worker: ";
   Employee::print();
 }
 // Fig. 10.1: hourlyl.h
 // Definition of class HourlyWorker
 #ifndef HOURLY1_H
 #define HOURLY1_H
 #include "employ2.h"
 
 class HourlyWorker : public Employee {
 public:
   HourlyWorker( const char *, const char *,
                 double = 0.0, double = 0.0);
   void setWage( double );
   void setHours( double );
   virtual double earnings() const;
   virtual void print () const;
 private:
   double wage;  // wage per hour
   double hours;  // hours worked for week
 } ;
 #endif
 // Fig. 10.1: hourly1.cpp
 // Member function definitions for class HourlyWorker
 #include < iostream.h>
 #include "hourly1.h"
 // Constructor for class HourlyWorker
 HourlyWorker::HourlyWorker( const char *first,
                             const char *last,
                             double w, double h }
   : Employee( first, last )  // call base-class constructor
 {
    setwage( w );
    setHours( h );
 }
 // Set the wage
 void HourlyWorker::setwage( double w )
   { wage = w > 0 ? w : 0; }
 // Set the hours worked
 void HourlyWorker::setHours( double h )
   { hours = h >= 0 && h < 168 ? h : 0; }
 // Get the HourlyWorker's pay
 double HourlyWorker::earnings() const
 {
   if ( hours <= 40 ) // no overtime
     return wage * hours;
   else            // overtime is paid at wage * 1.5
      return 40 * wage + ( hours - 4O ) * wage * 1.5;
 }
 // Print the HourlyWorker's name
 {
   cout << "\n   Hourly worker: ";
   Employee::print();
 }
 // Fig. 10.1: figl0_01.cpp
 // Driver for Employee hierarchy
 #include 
 #include 
 #include "employ2.h
 #include "boss1.h"
 #include "commis1.h"
 #include "piece1.h"
 #include "hourly1.h"
 void virtualViaPointer( const Employee * );
 void virtualViaReference( const Employee & );
 int main()
 {
    // set output formatting
   cout << setiosflags( ios::fixed | ios::showpoint )
        << setprecision( 2 );
   Boss b( "John", "Smith", 800.00 );
   b.print();                        // static binding
   cout << "earned $" << b.earnings();   // static binding
   virtualViaPointer( &b );        // uses dynamic binding
   virtualViaReferenee( b );       // uses dynamic binding
   CommissionWorker c( "Sue", "Jones", 200.0, 3.0, 150 );
   c.print();                        // static binding
   cout << "earned $" << c.earnings();   // static binding
   virtualViaPointer( &c )/        // uses dynamic binding
   virtualViaReference( c );       // uses dynamic binding
   PieceWorker p( "Bob", "Lewis", 2.5, 200 );
   p.print();                        // static binding
   cout << "earned $" << p.earnings();   // static binding
   virtualViaPointer( &p );        // uses dynamic binding
   virtualViaReference( p );       // uses dynamic binding
   HourlyWorker h( "Karen", "Price", 18.75, 40 );
   h.print();                        // static binding
   cout << "earned $" << h.earnings();   // static binding
   virtualViaPointer( &h );        // uses dynamic binding
   virtualViaReference( h );       // uses dynamic binding
   cout << endl;
   return 0;
 }
 // Make virtual function calls off a base-class pointer
 // using dynamic binding.
 void virtualViaPointer( const Employee *baseClassPtr )
 {
   baseClassPtr->print();
   cout << "earned $" << baseClassPtr->earnings();
 }
 // Make virtual function calls off a base-class reference
 // using dynamic binding.
 void virtualViaReference( const Employee &baseClassRef )
 {
   baseClassRef.print();
   cout << " earned $ " << baseclassRef.earnings();
 }

输出结果:

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函数,这也是动态关联的一个范例。