第5章继承与派生 继承是面向对象程序设计的第二个重要特性,通过继承实现了数据抽象基础上的代码重用。继承的作用是减少代码冗余,通过协调来减少接口和界面。 继承是面向对象程序设计的关键。它的好处非常多,最重要的有以下两点。 (1) 抽取对象类之间的共同点,消除冗余。仅仅用处于同一层次的类来构建软件是不可取的,实际上很多类之间都存在共同点,忽略这些共同点将会带来很大的冗余。 (2) 继承带来了软件的复用。用已经实现的类为基类,派生出新的类,可以做到“站在巨人的肩膀上”,能快速开发出高质量的程序。 继承反映了类的层次结构,并支持对事物从一般到特殊的描述。继承使得程序员可以以一个已有的较一般的类为基础建立一个新类,而不必从零开始设计。建立一个新的类,可以从一个或多个先前定义的类中继承数据成员和成员函数,而且可以重新定义或加进新的数据成员和成员函数,从而建立了类的层次或等级,这个新类称为派生类或子类,而先前有的类称为基类、超类或父类。 在C++语言中,有两种继承方式: 单一继承(参见图1.3单一继承示例)和多重继承(参见图1.4多重继承示例)。对于单一继承,派生类只能有一个直接基类; 对于多重继承,派生类可以有多个直接基类。 5.1单 一 继 承 5.1.1继承与派生的概念 C++语言的重要目标之一是代码重用。为了实现这个目标,C++语言采用两种方法: 对象成员和继承。在面向对象的程序设计中,大量使用继承和派生。类的继承实际上是一种演化、发展过程,即通过扩展、更改和特殊化,从一个已知类出发建立一个新类。通过类的派生可以建立具有共同关键特征的对象家族,从而实现代码重用。这种继承和派生的机制对于已有程序的发展和改进是极为有利的。 派生类同样也可以作为基类再派生新的类,这样就形成了类的层次结构。类的继承和派生的层次结构,可以说是人们对自然界中的事物进行分类、分析和认识的过程在程序设计中的体现。现实世界中的事物都是相互联系、相互作用的,人们在认识自然的过程中,根据事物的实际特征,抓住其共同特性和细小差别,利用分类的方法分析和描述这些实体或概念之间的相似点和不同点。 派生类具有如下特点。 (1) 新的类可在基类的基础上包含新的成员。 (2) 新的类中可隐藏基类的成员函数。 (3) 可为新类重新定义成员函数。 基类与派生类的关系如下。 (1) 派生类是基类的具体化。 (2) 派生类是基类定义的延续。 (3) 派生类是基类的组合。 5.1.2派生类的定义 在C++语言中,派生类的定义如下: class派生类名:[继承方式]基类名 { // 派生类成员声明 }; 其中: (1) 派生类名是新生成类的类名。 (2) 继承方式规定了如何访问从基类继承的成员。继承方式关键字为private、public和protected,分别表示私有继承、公有继承和保护继承,默认情况下是私有(private)继承。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限,将在5.1.3节中详细介绍。 (3) 派生类成员除了包括从基类继承来的所有成员之外,还包括新增加的数据成员和成员函数。这些新增加的成员正是派生类不同于基类的关键所在,是派生类对基类的发展。重用和扩充已有的代码,就是通过在派生类中新增成员来添加新的属性和功能的。 例如,定义如下的汽车类及其派生类: 小汽车类和卡车类。 class Vehicle//定义基类Vehicle { public://公有成员函数 void init_Vehicle(int in_wheels,float in_weight);//给数据成员初始化 intget_wheels(); //取车轮数 float get_weight(); //取汽车质量 float wheelloading(); //车轮承重 private://私有数据成员 int wheels; //车轮数 float weight; //表示汽车载重 }; 在基类Vehicle的基础上,定义了如下的派生类Car和Truck,在派生类中新增了一些数据成员和成员函数。 class Car:public Vehicle//定义派生类Car { public://新增公有成员函数 void intitialize(int in_wheels,float in_weight,int people=4); int passengers(); private: int passenger_load; //新增私有数据成员,表示载客数 }; class Truck:public Vehicle//定义派生类Truck { public://新增公有成员函数 void init_Truck(int,float); int passengers(); float weight_loads(); private://新增私有数据成员 int passenger_load; float weight_load; }; 在C++语言程序设计中,进行派生类的定义,给出该类的成员函数的实现之后,整个类就定义好了,这时就可以由它来生成对象,进行实际问题的处理。派生新类的过程,经历了三个步骤: 吸收基类成员、改造基类成员和添加新的成员,下面分别加以介绍。 1. 吸收基类成员 面向对象的继承和派生机制,其最主要的目的是实现代码的重用和扩充。吸收基类成员就是一个重用的过程,而对基类成员进行调整、改造以及添加新成员就是原有代码的扩充过程,二者是相辅相成的。 C++语言的类继承,首先是基类成员的全盘吸收,这样,派生类实际上就包含了它的所有基类的除构造函数和析构函数之外的所有成员。很多基类的成员,特别是非直接基类的成员,尽管在派生类中很可能根本不起作用,也被继承下来,在生成对象时要占据一定的内存空间,造成资源浪费,要对其进行改造。 2. 改造基类成员 对基类成员的改造包括两方面,一是依靠派生类的继承方式来控制基类成员的访问,二是对基类数据成员或成员函数的覆盖,即在派生类中定义一个和基类数据成员或成员函数同名的成员,由于作用域不同,产生成员覆盖(member overridden,又叫同名覆盖,即当一个已在基类中声明的成员名又在派生类中重新声明所产生的效果),基类中的成员就被替换成派生类中的同名成员。 成员覆盖使得派生类的成员掩盖了从基类继承得到的同名成员。这种掩盖既不是成员的丢失,也不是成员的重载。因为经类作用域声明后仍可引用基类的同名成员函数,而且可以由派生类的成员函数去引用从基类继承来的同名成员(参见例5.6),这是重载所没有的效果。基于这一成员覆盖的机制,可以充分体现派生的优越性。那就是派生类可以在继承基类的基础上继续扩充原设计中未考虑到的内容,从而使一个软件系统的生命周期大大地延长,这便是可重用软件设计思想的最终目标。 3. 添加新的成员 继承与派生机制的核心是在派生类中加入新的成员,程序员可以根据实际情况的需要,给派生类添加适当的数据成员和成员函数,来实现必要的新功能。在派生的过程中,基类的构造函数和析构函数是不能被继承下来的。同时,在派生类中,一些特殊的初始化和扫尾清理工作,也需要重新定义新的构造函数和析构函数。 5.1.3类的继承方式 在面向对象程序中,基类的成员可以有public(公有)、protected(保护)和private(私有)三种访问类型。在基类内部,自身成员可以对任何一个其他成员进行访问,但是通过基类的对象,就只能访问基类的公有成员。 派生类继承了基类的全部数据成员和除了构造函数、析构函数之外的全部成员函数,但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员,其访问属性由继承方式控制。 类的继承方式有public(公有)继承、protected(保护)继承和private(私有)继承三种。对于不同的继承方式,会导致基类成员原来的访问属性在派生类中有所变化。表5.1列出了不同继承方式下基类成员访问属性的变化情况。 表5.1不同继承方式下基类成员的访问属性 继承方式 访 问 属 性 publicprotectedprivate publicpublicprotected不可访问的 protectedprotectedprotected不可访问的 privateprivateprivate不可访问的 说明: 表5.1中第一列给出三种继承方式,第一行给出基类成员的三种访问属性,其余单元格内容为基类成员在派生类中的访问属性。 从表中可以看出以下几点: (1) 基类的私有成员在派生类中均是不可访问的,它只能由基类的成员访问。 (2) 在公有继承方式下,基类中的公有成员和保护成员在派生类中的访问属性不变。 (3) 在保护继承方式下,基类中的公有成员和保护成员在派生类中均为保护的。 (4) 在私有继承方式下,基类中的公有成员和保护成员在派生类中均为私有的。 注意: 保护成员与私有成员唯一的不同是当发生派生后,基类的保护成员可被派生类直接访问,而私有成员在派生类中是不可访问的。在同一类中私有成员和保护成员的用法完全一样。 1. 公有继承 公有继承方式创建的派生类对基类各种成员的访问权限如下。 (1) 基类公有成员相当于派生类的公有成员,即派生类可以像访问自身公有成员一样访问从基类继承的公有成员。 (2) 基类保护成员相当于派生类的保护成员,即派生类可以像访问自身的保护成员一样,访问基类的保护成员。 (3) 基类的私有成员,派生类内部成员无法直接访问。派生类使用者也无法通过派生类对象直接访问。 【例5.1】公有继承示例。 从基类Vehicle(汽车)公有派生Car(小汽车)类,Car类继承了Vehicle类的全部特征,同时,Car类自身也有一些特点,这就需要在继承Vehicle类时添加新的成员。 #include using namespace std; class Vehicle//基类Vehicle类的定义 { public://公有成员函数 Vehicle(int in_wheels,float in_weight) { wheels=in_wheels;weight=in_weight; } int get_wheels() { return wheels; } float get_weight() { return weight; } private://私有数据成员 float weight; int wheels; }; class Car:public Vehicle//派生类Car类的定义 { public://新增公有成员函数 Car(int in_wheel,float in_weight,int people=5):Vehicle(in_wheel,in_weight) { passenger_load=people; } int get_passengers() { return passenger_load; } private://新增私有数据成员 int passenger_load; }; int main() { Car car1(4,1000); //声明Car类的对象 cout<<"The message of car1(wheels,weight,passengers):"< using namespace std; class Vehicle//基类Vehicle类的定义 { public://公有成员函数 Vehicle(int in_wheels,float in_weight) { wheels=in_wheels;weight=in_weight; } int get_wheels() { return wheels; } float get_weight() { return weight; } private://私有数据成员 int wheels; float weight; }; class Car:private Vehicle//定义派生类Car类 { public://新增公有成员函数 Car(int in_wheels,float in_weight,int people=5):Vehicle(in_wheels,in_weight) { passenger_load=people; } int get_wheels() //重新定义get_wheels() { return Vehicle::get_wheels(); } float get_weight()//重新定义get_weight() { return Vehicle::get_weight(); } int get_passengers() { return passenger_load; } private://新增私有数据成员 int passenger_load; }; int main() { Car car1(4,1000);//定义Car类对象 cout<<"The message of car1(wheels,weight,passengers):"< using namespace std; class Vehicle//定义基类Vehicle { public://公有成员函数 Vehicle(int in_wheels,float in_weight) { wheels=in_wheels;weight=in_weight; } int get_wheels() { return wheels; } float get_weight() { return weight; } private://私有数据成员 int wheels; protected://保护数据成员 float weight; }; class Car:protected Vehicle//定义派生类Car { public://新增公有成员函数 Car(int in_wheels,float in_weight,int people=5):Vehicle(in_wheels,in_weight) { passenger_load=people; } int get_wheels()//重新定义get_wheels() { return Vehicle::get_wheels(); } float get_weight()//重新定义get_weight() { return weight; } int get_passengers() { return passenger_load; } private://新增私有数据成员 int passenger_load; }; int main() { Car car1(4,1000); //定义Car类的对象 cout<<"The message of car1(wheels,weight,passengers):"< using namespace std; class ST_COM { public: ST_COM(char *na, unsigned int n, float ma, float en, float ph): num(n),math(ma),english(en),physics(ph) { strcpy_s(name,na); } protected: char name[10]; unsigned int num; float mathematics,English,Physics; }; class EL_DEP:public ST_COM { public: EL_DEP(char *na,unsigned int n,float ma,float en,float ph,float pe,float el,float d): ST_COM(na,n,ma,en,ph), exchange(pe), elnet(el),dst(d) { } void show() { cout<<"Name:"< using namespace std; class Data { public: Data(int x) { Data::x=x; cout<<"class Data\n"; } private: int x; }; class A { public: A(int x):d1(x) { cout<<"class A\n"; } private: Data d1; }; class B:public A { public: B(int x):A(x),d2(x) { cout<<"class B\n"; } private: Data d2; }; class C:public B { public: C(int x):B(x) { cout<<"class C\n"; } }; int main() { C object(5); return 0; } 图5.3例5.5的运行结果 程序的运行结果如图5.3所示。 从程序运行的结果可以看出,构造函数的调用严格地按照先祖先、再客人、后自己的顺序执行。 【例5.6】类派生引出的成员覆盖的示例。 在例5.4的两个类中各插入一个同名的显示函数和用于记录平均成绩的数据成员avg来观察成员覆盖后的情形。 #include using namespace std; class ST_COM { public: ST_COM(char *na, unsigned int n, float ma, float en, float ph): num(n),math(ma),english(en),physics(ph) { strcpy_s(name,na); avg=(math+english+physics)/3; } void show() { cout<<"Name:"< using namespace std; class Person { public: Person() {cout<<"the constructor of class Person!\n";} ~Person() {cout<<"the destructor of class Person!\n"; } private: char *name; int age; char*address; }; class Student:public Person { public: Student() {cout<<"the constructor of class Student!\n";} ~Student() {cout<<"the destructor of class Student!\n";} private: char *department; int level; }; class Teacher:public Person { public: Teacher() {cout<<"the constructor of class Teacher!\n";} ~Teacher() {cout<<"the destructor of class Teacher!\n";} private: char *major; float salary; }; int main() { Student student1; Teacher teacher1; cout<<"------main function finished------"< using namespace std; class Bed { public: Bed(){} void sleep() {cout <<"sleeping...\n";} void setWeight(int i) {weight = i;} protected: int weight; }; class Sofa { public: Sofa(){} void watchTV() { cout <<"Watching TV.\n"; } void setWeight(int i) { weight =i; } protected: int weight; }; class SleeperSofa :public Bed, public Sofa//多重继承 { public: SleeperSofa(){} void foldOut(){ cout <<"Fold out the sofa.\n"; } }; int main() { SleeperSofa ss; ss.watchTV(); ss.foldOut(); ss.sleep(); ss.setWeight(20); //出现二义性 return 0; } 【程序解析】 ① 此例中SleeperSofa类是Bed类和Sofa类的公有派生类,而Bed类和Sofa类中均有成员函数setWeight(),对象ss调用setWeight()函数时存在二义性,所以当程序编译时将出现如图5.6所示的错误提示信息。 图5.6编译时的错误提示信息 ② 使用作用域运算符限定成员名可以消除上述二义性。例如: ss.Bed::setWeight(10); ss.Sofa::setWeight(10); (2) 如果一个派生类从多个基类中派生,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同基类中的成员时会产生二义性。要避免此种情况,可以利用5.3节讲到的虚基类。 2. 作用域规则 当基类中的成员名字在派生类中再次声明时,派生类中的名字就屏蔽掉基类中相同的名字(也就是派生类的自定义成员与基类成员同名时,派生类的成员优先)。如果要使用被屏蔽的成员,可由作用域操作符实现。它的形式是: 类名::类成员标识符。作用域操作符不仅可以用在类中,也可以用在函数调用时。 3. 支配规则 一个派生类中的名字将优先于它基类中相同的名字,这时二者之间不存在二义性,当选择该名字时,使用支配者(派生类中)的名字,称为支配规则。 5.2.3赋值兼容规则 所谓赋值兼容规则就是在公有派生的情况下,一个派生类的对象可以作为基类的对象来使用的地方(在公有派生的情况下,每一个派生类的对象都是基类的一个对象,它继承了基类的所有成员并没有改变其访问权限)。 具体地说,有三种情况可以把一个公有派生类的对象作为基类对象来使用。 (1) 派生类对象可以赋予基类的对象,例如(约定类derived是从类base公有派生而来的): Derived d; Base b; b=d; (2) 派生类对象可以初始化基类的引用,例如: Derived d; Base &br=d; (3) 派生类对象的地址可以赋予指向基类的指针,例如: Derived d; Base *pb=&d; 5.3虚基类 5.3.1虚基类的概念 当在多条继承路径上有一个公共的基类时,在这些路径中的某几条路径汇合处,这个公共的基类就会产生多个实例(或多个副本),若想只保存这个基类的一个实例,可以将这个公共基类说明为虚基类。从基类派生新类时,使用关键字virtual可以将基类说明成虚基类。一个基类,在定义它的派生类时,在作为某些派生类的虚基类的同时,又可以作为另一些派生类的非虚基类。 为了初始化基类的对象,派生类的构造函数要调用基类的构造函数。对于虚基类来讲,由于派生类的对象中只有一个虚基类对象。为保证虚基类对象只被初始化一次,这个虚基类构造函数必须只被调用一次。由于继承结构的层次可能很深,规定将在建立对象时所指定的类称为最直接派生类。虚基类对象是由最直接派生类的构造函数通过调用虚基类的构造函数进行初始化的。如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用,如果未被列出,则表示使用该虚基类的默认构造函数来初始化派生类对象中的虚基类对象。 C++语言规定,在一个成员初始化列表中出现对虚基类和非虚基类构造函数的调用,则虚基类的构造函数先于非虚基类的构造函数执行。 从虚基类直接或间接继承的派生类中的构造函数的成员初始化列表中都要列出这个虚基类构造函数的调用。但是,只有用于建立对象的那个派生类的构造函数调用虚基类的构造函数,而该派生类的基类中所列出的对这个虚基类的构造函数的调用在执行中被忽略,这样便保证了对虚基类对象只初始化一次。 【例5.9】利用虚基类避免产生二义性示例。 #include using namespace std; class Furniture//定义家具类 { public: Furniture(){} void setWeight(int i){ weight =i; } int getWeight(){ return weight; } protected: int weight; }; class Bed:virtual public Furniture//Furniture类作为Bed类的虚基类 { public: Bed(){} void sleep(){ cout <<"sleeping...\n"; } }; class Sofa :virtual public Furniture//Furniture类作为Sofa类的虚基类 { public: Sofa(){} void watchTV(){ cout <<"Watching TV.\n"; } }; class SleeperSofa :public Bed, public Sofa { public: SleeperSofa():Sofa(),Bed(){} void foldOut(){ cout <<"Fold out the sofa.\n"; } }; int main() { SleeperSofa ss; ss.watchTV(); ss.foldOut(); ss.sleep(); ss.setWeight(20); cout<<"weight:"< using namespace std; class Base { public: Base() {cout<<"This is Base class!\n";} }; class Base2 { public: Base2() {cout<<"This is Base2 class!\n";} }; class Level1:public Base2,virtual public Base { public: Level1() {cout<<"This is Level1 class!\n";} }; class Level2:public Base2,virtual public Base { public: Level2() {cout<<"This is Level2 class!\n";} }; class TopLevel:public Level1,virtual public Level2 { public: TopLevel() {cout<<"This is TopLevel class!\n";} }; int main() { TopLevel topObject; return 0; } 程序的运行结果如图5.9所示。 图5.9例5.10的运行结果 【例5.11】多重继承中构造函数和析构函数的调用顺序示例。 #include using namespace std; class OBJ1 { public: OBJ1() {cout<<"Constructing OBJ1"< 整体是一个类名,包括尖括号和int。通过这个类才可以产生对象(类的实例)。 对象是类的实例,而模板类是类模板的实例。 类模板由C++语言的关键字template引入,定义的语法形式如下: template class name { //类定义体 } template <返回类型><类名><类型名表>::<成员函数1>(形参表) { //成员函数定义体 } 其中,用尖括号括起来的是形式类属参数表,它列出类属类的每个形式类属参数,多个类属参数之间用逗号隔开,每个类属参数由关键字class或typename引入。 类模板必须用类型参数将其实例化为模板类后,才能用来生成对象。其表示形式一般为: 类模板名 <类型实参表> 对象名(值实参表) 其中,类型实参表表示将类模板实例化为模板类时所用到的类型(包括系统固有的类型和用户已定义类型),值实参表表示将该模板类实例化为对象时模板类构造函数所用到的变量。一个类模板可以用来实例化多个模板类。 下面通过一个简单的例子来介绍如何定义和使用类模板及模板类。 【例5.12】类模板和模板类的使用示例。 #include using namespace std; template class ClassTemplateTest { public: ClassTemplateTest(T); ~ClassTemplateTest() { cout<<"call deconstructor function"< ClassTemplateTest::ClassTemplateTest(T n) { size = n; cout<<"call constructor function"< object1(5); ClassTemplateTest object2(6.66); cout<<"---------------------------------"<开始,下面的类模板定义体部分与普通类定义的方法相同,只是在部分地方用参数T表示数据类型。 类模板的每一个非内联函数的定义是一个独立的模板,同样以template开始,函数头中的类名由类模板名加模板参数构成,如例子中的ClassTemplateTest 。 类模板在使用时必须指明参数,形式为: 类模板名<模板参数表>。主函数中的 ClassTemplateTest即是如此,编译器根据参数int生成一个实实在在的类,即模板类,理解程序时可以将ClassTemplateTest看成是一个完整的类名。在使用时ClassTemplateTest不能单独出现,它总是和尖括号中的参数表一起出现。 上面的例子中模板只有一个表示数据类型的参数,多个参数以及其他类型的参数也是允许的。 【例5.13】定义一个单向链表的模板类,分别实现增加、删除、查找和打印操作。 #include using namespace std; template//定义类模板 class List { public: List(); void addNode(T&); //增加结点 void removeNode(T&); //删除结点 T* findNode(T&); //查找结点 void printList(); //打印输出链表各结点值 ~List(); protected: structNode { Node* pNext; T* Pt; }; Node*pFirst; }; template List::List() { pFirst=0; } template void List::addNode(T& t) { Node* temp =new Node; temp->Pt =&t; temp->pNext = pFirst; pFirst = temp; } template void List::removeNode(T&t) { Node *q=0; if(*pFirst->Pt==t) { q=pFirst; pFirst=pFirst->pNext; } else { for(Node*p=pFirst;p->pNext;p=p->pNext) if(*(p->pNext->Pt)==t) { q=p->pNext; p->pNext=q->pNext; break; } } if(q) { cout<<"delete node of value "<Pt; delete q; } else cout<<"In removeNode function: No node of value "< T*List::findNode(T& t) { for(Node* p=pFirst;p;p=p->pNext) { if(*(p->Pt)==t) return p->Pt; } return0; } template void List::printList() { cout<<"链表中各结点值为: "; for(Node* p=pFirst;p;p=p->pNext) { if(p!=pFirst) cout<<"->"; cout<<*(p->Pt); } cout< List::~List() { Node* p=pFirst; while (p) { pFirst=pFirst->pNext; delete p->Pt; delete p; p=pFirst; } } int main() { List intList; //intList是模板类List的对象 for(int i=1;i<7;i++) { intList.addNode(*new int(i)); //向链表中插入结点 } intList.printList(); intb=9; int* pa=intList.findNode(b); if(pa) intList.removeNode(*pa); else cout<<"In main function: No code of value "< using namespace std; class Person { public: Person(char *name,int age,char sex) { strcpy_s(m_strName, name); m_nSex= (sex=='m'?0:1 ); m_nAge = age; } void ShowMe() { cout<<"姓名: "< using namespace std; class Employee//雇员类 { public: //设置雇员的基本信息 void setInfo(char *empname,int empsex,char*empid) { strcpy_s(name,empname); strcpy_s(emp_id,empid); } void getInfo(char*empname,char*empid)//取得雇员的基本信息 { strcpy_s(empname,strlen(name)+1,name); strcpy_s(empid,strlen(emp_id)+1,emp_id); } double getSalary()//取得所应得的总薪金数 { return salary; } protected: char name[10]; //姓名 char emp_id[8]; //职工号 double salary; //薪金数 }; class HourlyWorker:public Employee//时薪工类 { public: HourlyWorker() { hours=0; perHourPay=15.6; } int getHours()//取得某人工作的小时数 { return hours; } void setHours(int h)//设置某人工作的小时数 { hours=h; } double getperHourPay()//取得每小时应得的报酬 { return perHourPay; } void setperHourPay(double pay)//设置每小时应得的报酬 { perHourPay=pay; } void computePay()//计算工资 { if(hours<=40) salary=perHourPay*hours; else salary=perHourPay*40+(hours-40)*1.5*perHourPay; } protected: int hours; //工作的小时数 double perHourPay; //每小时应得的报酬 }; class PieceWorker:public Employee//计件工 { public: PieceWorker() { pieces=0; perPiecePay=26.8; } int getPieces() {return pieces;} void setPieces(int p)//设置生产的工件总数 {pieces=p;} double getperPiecePay() {return perPiecePay;} void setperPiecePay(double ppp) { perPiecePay=ppp;} void computePay() { salary=pieces*perPiecePay;} protected: int pieces; //每周所生产的工件数 double perPiecePay; //每个工件所应得的工资数 }; class Manager:public Employee//经理类 { public: void setSalary(double s)//设置经理的工资数 {salary=s;} }; class CommissionWorker:public Employee//佣金工类 { public: CommissionWorker() { baseSalary=500; total=0; percent=0.01; } double getBase() { return baseSalary;} void setbase(double base) { baseSalary=base;} double getTotal() { return total;} void setTotal(double t) { total =t;} double getPercent() { return percent;} double setPercent(double p) { percent=p;} void computePay() { salary=baseSalary+total*percent;} protected: double baseSalary; //保底工资 double total; //一周内的总销售额 double percent; //提成的额度 }; int main() { char name[10],emp_id[9]; HourlyWorker hworker; //时薪工 hworker.setInfo("John",0,"001"); hworker.setHours(65); hworker.getInfo(name,emp_id); hworker.computePay(); cout<<"------HourlyWorker------"< using namespace std; template class BinaryTree; template class Node { public: Node() { lChild=NULL;rChild=NULL; } Node(T data,Node *left=NULL,Node *right=NULL) { info=data; lChild=left; rChild=right; } friend class BinaryTree; private: Node *lChild,*rChild; T info; }; template class BinaryTree { public: BinaryTree()//构造函数 {root=NULL;} ~BinaryTree()//析构函数 {destroyTree(root);} void creatTree(T* data,int n); //建立(排序)二叉树 void inOrder() {inOrder(root);}//中序遍历 void removeNode(const T &data)//删除结点 {removeNode(data,root,root);} private: Node *root; //二叉树的根指针 void inOrder(Node *Current); //中序遍历 void insertNode(const T &data,Node * &b); //插入结点,第二个参数为引用 void removeNode(const T &data,Node * &a,Node * &b);//删除结点 void destroyTree(Node * Current); //删除树 }; template void BinaryTree::destroyTree(Node *Current) { if(Current!=NULL) { destroyTree(Current->lChild); destroyTree(Current->rChild); delete Current; //后序释放根结点 } } template void BinaryTree::insertNode(const T &data,Node * &b) { if(b==NULL)//空树,插入 { b=new Node(data); if(b==NULL) { cout<<"空间不足"<info)//小于,向左子树去插入 insertNode(data,b->lChild); else//大于或等于,向右子树去插入 insertNode(data,b->rChild); } template void BinaryTree::creatTree(T* data,int n)//建立一棵二叉排序树 { for(int i=0;i void BinaryTree::inOrder(Node *Current) { if(Current!=NULL)//递归终止条件 { inOrder(Current->lChild); //中序遍历左子树 cout<info<<""; //访问根结点,注意所放位置 inOrder(Current->rChild); //中序遍历右子树 } } template void BinaryTree::removeNode(const T &data,Node * &a,Node * &b) { Node *temp1,*temp2; if(b==NULL) return; if(datainfo) removeNode(data,b,b->lChild); //所查数小,去左子树 else if(data>b->info) removeNode(data,b,b->rChild); //所查数大,去右子树 else if (b->lChild!=NULL&&b->rChild!=NULL) { //查到值为data的结点,它有两个子树 temp2=b; temp1=b->rChild; //向右一步 if(temp1->lChild!=NULL) { while(temp1->lChild!=NULL) { //向左到极左的结点,将要用来取代被删除结点 temp2=temp1; temp1=temp1->lChild; } temp2->lChild=temp1->rChild; //把选中结点的右子树或NULL,接到该结点的父结点的左子树上 } else temp2->rChild=temp1->rChild; //向右一步后无左子树 b->info=temp1->info; delete temp1; } else {//只有一个子树或是叶结点 temp1=b; if(b->rChild!=NULL)//只有右子树 { temp1=b->rChild; b->info=temp1->info; b->rChild=temp1->rChild; b->lChild=temp1->lChild; } else if(b->lChild!=NULL)//只有左子树 { temp1=b->lChild; b->info=temp1->info; b->rChild=temp1->rChild; b->lChild=temp1->lChild; } else if(b==root) root=NULL; //叶结点,仅有根结点 else if(a->rChild==temp1) a->rChild=NULL; //被删除结点在父结点右边 else a->lChild=NULL; //被删除结点在父结点左边 delete temp1; } } int main() { const int n=15; int i,a[n]={10,5,15,8,3,18,13,12,14,16,20,1,4,6,9}; BinaryTree btree; btree.creatTree(a,n); cout<<"输入数据: "< btree1; btree1.creatTree(a1,1); cout<<"\n输入数据: "<<'\t'<