当前位置: 首页 > 文档资料 > C++大学教程 >

9.6 在派生类中重定义基类成员

优质
小牛编辑
174浏览
2023-12-01

派生类可以通过提供同样签名的新版本(如果签名不同,则是函数重载而不是函数重定义)重新定义基类成员函数。派生类引用该函数时会自动选择派生类中的版本。作用域运算符可用来从派生类中访问基类的该成员函数的版本。

常见编程错误 9.3
派生类中重新定义基类的成员函数时,为完成某些附加工作.派生类版本通常要调用基类中的该函数版本。不使用作用域运算符会由于派生类成员函数实际上调用了自身而引起无穷递归。这样会使系统用光内存,是致命的运行时错误。

考察一个简单的类 Employee,它存储雇员的姓(成员 firstName)和名(成员 lastName)。这种信息对于所有雇员(包括Employee的派生类的雇员)是很普遍的。现在假设从雇员类Employee派生出了小时工类HourlyWorker、计件工类PieceWorker、老板类Boss和销售员类CommissionWorker。

小时工每周工作40小时,超过40小时部分的报酬是平时的1.5倍;计件工是按生产的工作计算报酬的,每件的报酬是固定的,假设他只生成一种类型的工件,因而类PieceWorker的private数据成员是生产的工件数量和每件的报酬;老板每周有固定的薪水;销售员每周有小部分固定的基本工资加上其每周销售额的固定百分比。为简单起见,此处只研究类Empbyee和派生类HourlyWorker。

本章的第二个例子见图 9.5。第1行到47行分别是类Employee的定义和其成员函数的定义,第48行到94行分别是类HoudyWorker的定义和其成员函数的定义,第95行到结束是类继承层次 Employee/HourlyWorker 的驱动程序,该程序很简单,仅仅建立并初始化了类 HourlyWorker 的对象,然后调用类HourlyWorker的成员函数print输出对象的数据。

1 // Fig. 9.5: employ.h
2 // Definition of class Employee
3 #ifndef EMPLOY_H
4 #define EMPLOY_H
5
6 class Employee {
7 public:
8 Employee( const char *, const char * ); // constructor
9 void print() const; // output first an last name
10 ~Employee(); // destructor
11 private:
12 char *firstName; // dynamically allocated string
13 char * lastName; // dynamically allocated string
14 } ;
15
16 #endif
17 // Fig. 9.5: employ.cpp
18 // Member function definitions for class Employee
19 #include <string.h>
21 #include <assert.h>
22 #include "employ.h"
23
24 // Constructor dynamically allocates space for the
25 // first and last name and uses strcpy to copy
26 // the first and last names into the object.
27 Employee::Employee( const char *first, const char *last )
28 {
29 firstName = new char( strlen( first ) + 1);
30 assert( firstName != 0 ); // terminate if not allocated
31 strcpy( firstName, first );
32
33 lastName = new char( strlen( last ) + 1 );
34 assert( lastName != 0 ); // terminate if not allocated
35 strcpy( lastName, last );
36 }
37
38 // Output employee name
39 void Employee::print() const
40 {cout << firstName << ' ' << lastName; }
41
42 // Destructor deallocates dynamically allocated memory
43 Employee::~Employee()
44 {
45 delete [] firstName; // reclaim dynamic memory
46 delete [] lastName; // reclaim dynamic memory
47 }
48 // Fig. 9.5: hourly.h
49 // Definition of class HourlyWorker
50 #ifndef HOURLY_H
51 #define HOURLY_H
52
53 #include "employ.h"
54
55 class HourlyWorker : public Employee {
56 public:
57 HourlyWorker( const char*, const char*, double, double );
58 double getPayO const; // calculate and return salary
59 void print() const; // overridden base-class print
60 private:
61 double wage; // wage per hour
62 double hours; // hours worked for week
63 };
64
65 #endif
66 // Fig. 9.5: hourly.cpp
67 // Member function definitions for class HourlyWorker
68 #include <iostream.h>
69 #include <iomanip.h>
70 #include "hourly.h"
71
72 // Constructor for class HourlyWorker
73 HourlyWorker::HourlyWorker(constchar*first,
74 const char *last,
75 double initHours, double initwage )
76 : Employee( first, last ) // call base-class constructor
77 {
70 hours = initHours; // should validate
79 wage = initWage; // should validate
80 }
81
82 // Get the HourlyWorker's pay
83 double HourlyWorker::getPay() const { return wage * hours; }
84
85 // Print the HourlyWorker's name and pay
86 void HourlyWorker::print() const
87 {
88 cout << "HourlyWorker::print() is executing\n\n";
89 Employee::print(); // call base-class print function
90
91 cout <<" is an hourly worker with pay of $"
92 << setiosflags( ios::fixed | ios::showpoint )
93 << setprecision( 2 ) << getPay() << endl;
94 }
95 // Fig. 9.5: fig.09_05.cpp
96 // Overriding a base-class member function in a
97 // derived class.
98 #include <iostream.h>
99 #include "hourly.h"
100
101 int main()
102 {
103 HourlyWorker h( "Bob", "Smith", 40.0, 10.00 );
104 h.print();
105 return 0;
106 }

输出结果:

HourlyWorker::print() is executing
Bob Smith is an hourly worker with pay of $400.00

图 9.5 在派生类中重新定义基类的成员函数

类 Employee 的定义由两个 private char* 类型的数据成员(fisttName和lastName)和三个成员函数(构造函数、析构函数和print函数)组成。构造函数接收两个字符串,并动态分配存储字符串的字符数组。宏assert(见第18章)用来确定是否为firstName和lastName分配了内存。

如果没有,程序终止并返回一条出错信息,该信息指出了被测试的条件以及条件所在的行号和文件。由于Employee的数据是private类型,所以只能用成员函数print访问数据,函数print非常简单,仅仅输出雇员的姓和名。析构函数将动态分配的内存交还给系统(防止内存泄漏)。

类HoudyWorker对类Employee的继承是public继承。类定义的第一行指定了这种继承方式:

class HourlyWorker:public EmPloyee

HourlyWorker的public接口包括Employee的函数print和HourlyWorker的成员函数getPay和print。注意,类HourlyWorker定义了其自身的print函数(使用同样的函数原型Employee:print()),所以类HourlyWorker有权访问两个print函数。类HourlyWorker还包含用来计算雇员的每周薪水的private数据成员wage和hours。

HourlyWorker的构造函数用成员初始化值语法将字符串first和last传递给Employee的构造函数,从而初始化了基类的成员,然后再初始化成员wage和hours。成员函数getPay用来计算HourlyWorker的工资。

类HourlyWorker的成员函数print重新定义Employee的print成员函数。为提供更多的功能而在派生类中重新定义基类的成员函数是常有的事。被重新定义的函数有时候为执行一些新任务而要调用基类中的函数版本。在本例中,派生类函数print调用基类Employee的print函数输出了雇员的名字(基类print函数是惟一能访问该类private数据的函数),派生类的print函数输出了雇员的工资。

调用基类print函数的方法如下:

Employee::print();

因为基类函数和派生类函数的名字相同,所以必须在基类函数前使用基类名和作用域运算符,否则将调用派生类的函数版本(即类HourlyWorker的print函数调用其自身),从而导致无穷递归。