1、1第第 8 8章章 继承与派生继承与派生8.1 8.1 派生类的定义及其构造和析构函数派生类的定义及其构造和析构函数8.2 8.2 派生类使用例派生类使用例-公司雇员档案的管理公司雇员档案的管理8.3 8.3 多态性与虚函数多态性与虚函数8.4 8.4 虚函数使用例虚函数使用例1-1-计算函数的定积分计算函数的定积分8.5 8.5 与基类对象和派生类对象相关的赋值兼容性问题与基类对象和派生类对象相关的赋值兼容性问题8.6 8.6 虚函数使用例虚函数使用例2-2-利用图元类画图利用图元类画图8.7 8.7 派生关系中的二义性处理派生关系中的二义性处理8.8 8.8 虚基类虚基类2第第 8 8章章
2、 继承与派生继承与派生8.18.1 公司雇员档案的管理公司雇员档案的管理8.28.2 派生类的说明及其构造和析构函数派生类的说明及其构造和析构函数8.3 8.3 其他特征的继承关系其他特征的继承关系8.48.4 派生关系中的二义性处理派生关系中的二义性处理8.58.5 虚基类虚基类8.68.6 多态性与虚函数多态性与虚函数8.78.7 虚函数使用实例虚函数使用实例3 C+C+程序用不同的类定义来表示一组数据以及对这些数据的操作与处理,程序用不同的类定义来表示一组数据以及对这些数据的操作与处理,而类之间往往具有某种关系,而类之间往往具有某种关系,“继承与派生继承与派生”就是类间的一种常用关系。就
3、是类间的一种常用关系。例如,例如,交通工具交通工具 汽车汽车 轿车轿车 红旗轿车红旗轿车。具有层次关系!具有层次关系!汽车汽车 是一种特殊的是一种特殊的 交通工具交通工具 轿车轿车 是一种特殊的是一种特殊的 汽车汽车 红旗轿车红旗轿车 是一种特殊的是一种特殊的 轿车轿车在这个层次结构中,由上到下,是一个具体化、特殊化的过程;由下到上,是一个抽象化的过程。上下层之间的关系就可以看作是基类与派生类的关系。4 只要定义清楚了只要定义清楚了“交通工具交通工具”,那么在定义,那么在定义“汽车汽车”时时(注意,它就是交通工具),只需再说明它的特殊性(而不(注意,它就是交通工具),只需再说明它的特殊性(而不
4、必重新从头定义!)。必重新从头定义!)。同理,只要定义清楚了同理,只要定义清楚了“轿车轿车”,那么在定义,那么在定义“红旗轿车红旗轿车”时(注意,它就是轿车),只需再说明它的特殊性(根本不时(注意,它就是轿车),只需再说明它的特殊性(根本不必重新从头定义!)。必重新从头定义!)。又例如,又例如,公司四种雇员档案的管理:公司四种雇员档案的管理:employeeemployee(雇员):雇员):姓名、年龄、工资;姓名、年龄、工资;managermanager(经理):经理):姓名、年龄、工资、姓名、年龄、工资、行政级别;行政级别;engineerengineer(工程师):工程师):姓名、年龄、工
5、资、姓名、年龄、工资、专业、学位;专业、学位;directordirector(高级主管):高级主管):姓名、年龄、工资、姓名、年龄、工资、专业、学位、职务。专业、学位、职务。5 C+C+提供了类定义的派生和继承功能,能很好提供了类定义的派生和继承功能,能很好地解决上述问题(使代码可重用,避免重复!)。地解决上述问题(使代码可重用,避免重复!)。若类若类A A是类是类B B的的基类(父类)基类(父类),则类,则类B B是类是类A A的的派派生类(子类)生类(子类)。也可以说,类也可以说,类B B(子类子类)继承了继承了类类A A(父类父类););或说,类或说,类A A(父类父类)派生出派生出类
6、类B B(子类子类)。)。6n 所谓继承,就是新的类从已有类那里得到已有的特性。从另一个角度来看,从已有类产生新类的过程就是类的派生。已有的类称为基类或父类,产生的新类称为派生类或子类。派生类同样也可以作为基类再派生新的类,这样就形成了类的层次结构。7派生类说明也是类说明派生类说明也是类说明 class class :private:private:;public:public:;protected:protected:;其中:其中:基类说明表:列出所给类的基类;基类说明表:列出所给类的基类;派生类名是新生成的类名l1.1.派生类的定义派生类的定义派生类成员声明;派生类成员声明;8 的一般格式
7、为:的一般格式为:1,.,n 而而 又可为又可为privateprivate、publicpublic或或protectedprotected三个关键字之一,由它指定各基三个关键字之一,由它指定各基类的被继承方式。类的被继承方式。如果不显式地给出继承方式关键字,系统的默认值就认为是私有继承(private)。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限,9派生类成员指除了从基类继承来的所有派生类成员指除了从基类继承来的所有成员之外,新增加的数据和函数成员。成员之外,新增加的数据和函数成员。这些新增的成员正是派生类不同于基类这些新增的成员正是派生类不同于基类的关键所在
8、,是派生类对基类的发展。的关键所在,是派生类对基类的发展。当重用和扩充已有的代码时,就是通过当重用和扩充已有的代码时,就是通过在派生类中新增成员来添加新的属性和在派生类中新增成员来添加新的属性和功能。可以说,这就是类在继承基础上功能。可以说,这就是类在继承基础上的进化和发展。的进化和发展。108.2 8.2 派生类使用例派生类使用例-公司雇员档案的管理公司雇员档案的管理 -参看书参看书p251,8.1p251,8.1节节 假设公司雇员分为:雇员假设公司雇员分为:雇员(employee)employee)、经理经理(manager)manager)、工程师工程师(engineer)enginee
9、r)、高级主管高级主管(director)director)。而且假定只关心这几类雇员各自的如下一些数据:而且假定只关心这几类雇员各自的如下一些数据:employee employee(雇员):雇员):姓名、年龄、工资;姓名、年龄、工资;managermanager(经理):经理):姓名、年龄、工资、姓名、年龄、工资、行政级别;行政级别;engineerengineer(工程师):工程师):姓名、年龄、工资、姓名、年龄、工资、专业、学位;专业、学位;directordirector(高级主管):高级主管):姓名、年龄、工资、姓名、年龄、工资、专业、学位、职务。专业、学位、职务。11 (具体程序
10、见书具体程序见书p251)p251)#include#include#include#include class employee class employee /employee /employee类类(类型类型),将作为其它几个类的基类,将作为其它几个类的基类 short age;short age;float salary;float salary;protected:protected:char char*name;name;12public:public:employee(short employee(short agag,float,float sasa,char,char*nan
11、a)age=age=agag;salary=salary=sasa;name=new charstrlen(na)+1;name=new charstrlen(na)+1;strcpy(name,nastrcpy(name,na););void print()const void print()constcoutcout name:;name:;coutcoutage:;age:;coutcoutsalarysalaryendlendl;employee()deletename;employee()deletename;13class managerclass manager:public e
12、mployee public employee/派生类派生类intint level;level;public:public:manager(short manager(short agag,float,float sasa,char,char*nana,intint levlev):employee(employee(ag,sa,naag,sa,na)/对基类初始化负责对基类初始化负责level=level=levlev;void print()const void print()const employee:printemployee:print();();/调用基类调用基类printpr
13、int显示显示“共性共性”数据数据coutcout level:level level:levelendlendl;14 /*注意:允许派生类中的注意:允许派生类中的printprint与基类的与基类的printprint重名,按如下规重名,按如下规定进行处理:对子类而言,不加类名限定时默认为是处理子类定进行处理:对子类而言,不加类名限定时默认为是处理子类成员,而要访问父类重名成员时,则要通过类名限定成员,而要访问父类重名成员时,则要通过类名限定*/class engineer:public employee class engineer:public employee char char s
14、peciality,adegreespeciality,adegree;public:public:.;enumenum ptitleptitle PS,GM,VPS,VGM;PS,GM,VPS,VGM;class director:public manager class director:public manager ptitleptitle post;post;public:public:.;15void main()void main()/主函数主函数employee emp1(23,610.5,zhang),employee emp1(23,610.5,zhang),emp2(27,
15、824.75,zhao);emp2(27,824.75,zhao);manager man1(32,812.45,li,11),manager man1(32,812.45,li,11),man2(34,1200.5,cui,7);man2(34,1200.5,cui,7);engineer eng(26,1420.10,meng,E,M);engineer eng(26,1420.10,meng,E,M);director dir(38,1800.2,zhou,2,GM);director dir(38,1800.2,zhou,2,GM);emp1.print();emp1.print();
16、emp2.print();emp2.print();man1.print();man1.print();man2.employee:print();man2.employee:print();/调用基类的调用基类的print print eng.print();eng.print();dir.print();dir.print();16程序执行后的显示结果如下:程序执行后的显示结果如下:zhangzhang:23:610.5:23:610.5 zhaozhao:27:824.75:27:824.75 lili:32:812.45:32:812.45 level:11 level:11 cui:
17、34:1200.5 cui:34:1200.5 mengmeng:26:1420.1:26:1420.1 speciality:Especiality:E academic degree:M academic degree:M zhouzhou:38:1800.2:38:1800.2 level:2 level:2 post:1 post:117 8.8.2 2 派生类的定义及其构造和析构函数派生类的定义及其构造和析构函数 -参看书参看书p255-262,8.2.1p255-262,8.2.1与与8.2.28.2.2小节小节1.1.派生类的定义派生类的定义class class :privat
18、e:private:;public:public:;protected:protected:;18 的一般格式为:的一般格式为:1,.,n 而而 又可为又可为privateprivate、publicpublic或或protectedprotected。19 派生方式派生方式(基类基类 在基类中的在基类中的 在派生类中在派生类中 的被继承方式的被继承方式)存取权限存取权限 的存取权限的存取权限=publicpublicpublicpublicpublicpublicpublicpublicpotectedpotected protectedprotectedpublicpublicprivat
19、eprivate(inaccessible)(inaccessible)potectedpotected publicpublicpotectedpotectedpotectedpotectedpotectedpotected protectedprotectedpotectedpotectedprivateprivate(inaccessible)(inaccessible)privateprivatepublicpublicprivateprivateprivateprivatepotectedpotected privateprivateprivateprivateprivatepriv
20、ate(inaccessible)(inaccessible)=20 注意:注意:public public派生方式:派生方式:使基类的公有成员和保护成员在派使基类的公有成员和保护成员在派生类中仍然是公有成员和保护成员,而基类的私有成员不生类中仍然是公有成员和保护成员,而基类的私有成员不可在派生类中被存取。可在派生类中被存取。protected protected派生方式:派生方式:使基类的公有成员和保护成员在使基类的公有成员和保护成员在派生类中都变为保护成员,而基类的私有成员不可在派生派生类中都变为保护成员,而基类的私有成员不可在派生类中被存取。类中被存取。privateprivate派生方
21、式:派生方式:使基类的公有成员和保护成员在派使基类的公有成员和保护成员在派生类中都变为私有成员,而基类的私有成员不可在派生类生类中都变为私有成员,而基类的私有成员不可在派生类中被存取。中被存取。21派生类中可出现四种成员:派生类中可出现四种成员:1)1)不可访问的成员不可访问的成员-基类的基类的privateprivate私有成员被继私有成员被继承过来后,这些成员在派生类中是不可访问的。承过来后,这些成员在派生类中是不可访问的。2)2)私有成员私有成员-包括在派生类中新增加的包括在派生类中新增加的privateprivate私私有成员以及从基类私有继承过来的某些成员。这些成员在有成员以及从基类
22、私有继承过来的某些成员。这些成员在派生类中是可以访问的。派生类中是可以访问的。3)3)保护成员保护成员-包括在派生类中新增加的包括在派生类中新增加的potectedpotected保保护成员以及从基类继承过来的某些成员。这些成员在派生护成员以及从基类继承过来的某些成员。这些成员在派生类中是可以访问的。类中是可以访问的。4)4)公有成员公有成员-包括在派生类中新增加的包括在派生类中新增加的publicpublic公有公有成员以及从基类公有继承过来的基类的成员以及从基类公有继承过来的基类的publicpublic成员。这些成员。这些成员不仅在派生类中可以访问,而且在建立派生类对象的成员不仅在派生类
23、中可以访问,而且在建立派生类对象的模块中,也可以通过对象来访问它们。模块中,也可以通过对象来访问它们。22 分析下述程序中的继承与派生关系,并上机进行分析下述程序中的继承与派生关系,并上机进行测试验证,以加深对它们进行正确使用的理解。测试验证,以加深对它们进行正确使用的理解。class class baseClabaseCla intint privDataprivData;protected:protected:intint protDataprotData;public:public:intint publDatapublData;23class class publDrvClapublD
24、rvCla:public :public baseClabaseCla public:public:void void usebaseClaDatausebaseClaData()()publDatapublData=11;/OK!=11;/OK!protDataprotData=12;=12;/OK!/OK!privDataprivData=13;=13;/ERROR!/ERROR!;24 class claD21:public class claD21:public publDrvClapublDrvCla public:public:void void usebaseClaDatause
25、baseClaData()()publDatapublData=111;=111;/OK!/OK!protDataprotData=121;=121;/OK!/OK!privDataprivData=131;=131;/ERROR!/ERROR!;25 class class protDrvClaprotDrvCla:protected :protected baseClabaseCla public:public:void void usebaseClaDatausebaseClaData()()publDatapublData=21;/OK!=21;/OK!protDataprotData
26、=22;=22;/OK!/OK!privDataprivData=23;=23;/ERROR!/ERROR!;26class claD22:public class claD22:public protDrvClaprotDrvCla public:public:void void usebaseClaDatausebaseClaData()()publDatapublData=211;=211;/OK!/OK!protDataprotData=221;=221;/OK!/OK!privDataprivData=231;=231;/ERROR!/ERROR!;27 class class pr
27、ivDrvClaprivDrvCla:private :private baseClabaseCla public:public:void void usebaseClaDatausebaseClaData()()publDatapublData=31;=31;/OK!/OK!protDataprotData=32;=32;/OK!/OK!privDataprivData=33;=33;/ERROR!/ERROR!;28class claD23:public class claD23:public privDrvClaprivDrvCla public:public:void void use
28、baseClaDatausebaseClaData()()publDatapublData=311;=311;/ERROR!/ERROR!protDataprotData=321;=321;/ERROR!/ERROR!privDataprivData=331;=331;/ERROR!/ERROR!;29void main()void main()baseClabaseCla ob0;ob0;ob0.publData=1;ob0.publData=1;/OK!/OK!ob0.protData=2;ob0.protData=2;/ERROR!/ERROR!ob0.privData=3;ob0.pr
29、ivData=3;/ERROR!/ERROR!claD21 d21;claD21 d21;claD22 d22;claD22 d22;claD23 d23;claD23 d23;d21.publData=4;d21.publData=4;/OK!/OK!d21.protData=5;d21.protData=5;/ERROR!/ERROR!d21.privData=6;d21.privData=6;/ERROR!/ERROR!d22.publData=7;d22.publData=7;/ERROR!/ERROR!d22.protData=8;d22.protData=8;/ERROR!/ERR
30、OR!d22.privData=9;d22.privData=9;/ERROR!/ERROR!d23.publData=7;d23.publData=7;/ERROR!/ERROR!d23.protData=8;d23.protData=8;/ERROR!/ERROR!d23.privData=9;d23.privData=9;/ERROR!/ERROR!30图7-2 多继承和单继承(a)多继承(b)单继承 318.2派生类的构造函数和析构函数 不是所有的函数都能自动地从基类继承到不是所有的函数都能自动地从基类继承到派生类中的。构造函数和析构函数是用派生类中的。构造函数和析构函数是用来处理对象
31、的创建和析构的,它们只知来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么。道对在它们的特殊层次的对象做什么。所以,在整个层次中的所有的构造函数所以,在整个层次中的所有的构造函数和析构函数都必须被调用,也就是说,和析构函数都必须被调用,也就是说,构造函数和析构函数不能被继承。构造函数和析构函数不能被继承。328.2 派生类的构造和析构函数n 8.2.1 构造函数n 派生类对象的初始化也是通过派生类的构造函数实现的。具体来说,就是对该类的数据成员赋初值。派生类的数据成员由所有基类的数据成员与派生类新增的数据成员共同组成,如果派生类新增成员中包括有内嵌的其它类对象,派生类的数据成员
32、中实际上还间接包括了这些对象的数据成员。33n 因此,初始化派生类的对象时,就要对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。因此,派生类的构造函数需要以合适的初值作为参数,隐含调用基类和新增的内嵌对象成员的构造函数来初始化它们各自的数据成员,然后再加入新的语句对新增普通数据成员进行初始化。34 2.2.派生类的构造函数和析构函数派生类的构造函数和析构函数 派生类的构造函数的一般格式如下:派生类的构造函数的一般格式如下:():而而 按如下格式构成:按如下格式构成:()1),.,()n),()1),.,()m)(注:若无对象成员时,则不出现此后半部分;基类名与注:若无对象成员时,则
33、不出现此后半部分;基类名与对象成员名的次序无关紧要,各自出现的顺序可以任意)对象成员名的次序无关紧要,各自出现的顺序可以任意)35 其中:派生类的构造函数名与派生类名相同。参数总表需要列出初始化基类数据、新增内嵌对象数据及新增一般成员数据所需要的全部参数。冒号之后,列出需要使用参数进行初始化的基类名和内嵌成员名及各自的参数表,各项之间用逗号分隔。36 派生类构造函数执行的一般次序如下:派生类构造函数执行的一般次序如下:(1)(1)调用各基类的构造函数,调用调用各基类的构造函数,调用顺序继承顺序继承时的声明顺序。时的声明顺序。(从左到右从左到右)(2)(2)若派生类含有对象成员的话,调用各对若派
34、生类含有对象成员的话,调用各对象成员的构造函数,象成员的构造函数,调用顺序按照声明顺序调用顺序按照声明顺序。(3)(3)执行派生类构造函数的函数体。执行派生类构造函数的函数体。析构派生类对象析构派生类对象时,其执行次序恰与构造时时,其执行次序恰与构造时的次序相反。的次序相反。派生类构造函数的执行顺序一般是,先祖先(基类),再客人(内嵌对象),后自己(派生类本身)。37#includeclassCBintb;public:CB(intn)b=n;coutCB:b=bendl;CB()coutCBobjisdestrctingendl;classCCintc;public:CC(intn1,int
35、n2)c=n1;coutCC:c=cendl;CC()coutCCobjisdestructingendl;38class CD:public CB,public CCclass CD:public CB,public CC intint d;d;public:public:CD(intCD(int n1,int n2,int n3,int n4)n1,int n2,int n3,int n4):CC(n3,n4),CB(n2):CC(n3,n4),CB(n2)d=n1;d=n1;coutcoutCD:d=dCD:d=dendlendl;CD()coutCD()coutCDobjCDobj i
36、s destructing is destructingendlendl;void main(void)void main(void)CD CDobj(2,4,6,8);CD CDobj(2,4,6,8);39运行结果为:运行结果为:CB:b=4CB:b=4CC:c=6CC:c=6CD:d=2CD:d=2CDobjCDobj is destructing is destructingCCobjCCobj is destructing is destructingCBobjCBobj is is destrctingdestrcting 思考:思考:将派生类将派生类CDCD改写为如下形式后,请给
37、出输出结果。改写为如下形式后,请给出输出结果。40class CA int a;public:CA(int n)a=n;coutCA:a=aendl;CA()coutCAobj is destructing.endl;class CB int b;public:C B(i n t n)b=n;coutCB:b=bendl;CB()coutCBobj is destrcting.endl;class CC:public CA int c;public:CC(int n1,int n2):CA(n2)c=n1;coutCC:C=cendl;CC()coutCBobj is destructinge
38、ndl;class CD:public CB,public CC int d;public:CD(int n1,int n2,int n3,int n4):CC(n3,n4),CB(n2)d=n1;coutCD:d=dendl;CD()coutCDobj is destructingendl;void main(void)CD CDobj(2,4,6,8);41这个程序只有一个对象说明语句,但却要运行四个这个程序只有一个对象说明语句,但却要运行四个构造函数和四个析构函数。从下面的运算结果,构造函数和四个析构函数。从下面的运算结果,可以看出调用这些函数的先后次序。输出为:可以看出调用这些函数的先
39、后次序。输出为:CB:b=4CB:b=4 CA:a=8 CA:a=8 CC:c=6 CC:c=6 CD:d=2 CD:d=2 CDobjCDobj is destructing is destructing CCobjCCobj is destructing is destructing CAobjCAobj is destructing is destructing CBobjCBobj is destructing is destructing 42从这个例子可知:从这个例子可知:()派生类构造函数的参数不仅要为自己的数()派生类构造函数的参数不仅要为自己的数据成员提供初始化数据,还要为基
40、类,以及基据成员提供初始化数据,还要为基类,以及基类的基类提供初始化数据。类的基类提供初始化数据。()由初始化符表指明哪些参数用于本类,哪()由初始化符表指明哪些参数用于本类,哪些参数用于基类。些参数用于基类。在多数多重继承情况下,初始化工作先基类,多在多数多重继承情况下,初始化工作先基类,多个基类则按自左至右的顺序,再对象成员,最个基类则按自左至右的顺序,再对象成员,最后是自身。如果基类又是一个派生类,那么它后是自身。如果基类又是一个派生类,那么它的初始化又同样按本条指出的顺序,这是个递的初始化又同样按本条指出的顺序,这是个递归过程归过程 43调用成员函数n派生类中的成员函数与基类中的成员函
41、数可以有相派生类中的成员函数与基类中的成员函数可以有相同的名称同的名称 n当使用基类的对象调用函数时,基类的函数被调用当使用基类的对象调用函数时,基类的函数被调用 n当使用派生类对象的名称时,派生类的函数被调用当使用派生类对象的名称时,派生类的函数被调用 n如果派生类的成员函数要调用相同名称的基类函数如果派生类的成员函数要调用相同名称的基类函数,它必须使用作用域运算符它必须使用作用域运算符44【例8-4】派生类的构造函数和析构函数(多继承,含有内嵌对象)例题。有三个基类Base1、Base2和Base3,它们都有自己的构造函数和析构函数。其中Base3有一个默认的构造函数,即不带参数的构造函数
42、,其余两个的构造函数都带有参数。类Derive由这三个基类经过公有派生而来。派 生 类 新 增 加 了 三 个 私 有 对 象 成 员,memberBase1、memberBase2和memberBase3,它们分别是Base1、Base2和Base3类的对象。另外,派生类定义了自己的构造函数,而没有定义析构函数,即采用默认的析构函数。45程序代码如下:#includeclassBase1/基类Base1,构造函数有参数public:Base1(inti)coutconstructing Base1iendl;B a s e 1()c o u t d e s t r u c t i n g B
43、ase1endl;/Base1的析构函数;46Class Base2/基类Base2,构造函数有参数public:Base2(int j)coutconstructing Base2jendl;Base2()coutdestructing Base2endl;/Base2的析构函数;Class Base3/基类Base3,构造函数无参数 47public:Base3()coutconstructing Base3endl;Base3()coutdestructing Base3endl;/Base3的析构函数;Class Derive:public Base2,public Base1,pub
44、lic Base3/派生新类private:/派生类新增私有对象成员48 Base1 memberBase1;Base2 memberBase2;Base3 memberBase3;public:/派生类的构造函数D e r i v e(i n t a,i n t b,i n t c,i n t d):Base2(b),memberBase2(d),memberBase1(c),Base1(a);Void main()Derive object(2,4,6,8);49 程序运行结果为 constructingBase24 constructingBase12 constructingBase3
45、constructingBase16constructingBase28constructingBase3destructingBase3destructingBase2destructingBase1destructingBase3destructingBase1destructingBase250调用成员函数(续)n基类中的函数既可使用基类的对象,也可使用派生类的对象调用n如果函数存在于派生类而不是基类中,那么它只能被派生类的对象调用518.3 8.3 其他特征的继承关系其他特征的继承关系1 1友元关系友元关系基类的友元不继承。即,如果基类有友元类或友元函数,则基类的友元不继承。即,如果基
46、类有友元类或友元函数,则其派生类不因继承关系也有此友元类或友元函数。其派生类不因继承关系也有此友元类或友元函数。另一方面,如果基类是某类的友元,则这种友元关系是被继另一方面,如果基类是某类的友元,则这种友元关系是被继承的。即,被派生类继承过来的成员,如果原来是某类的友承的。即,被派生类继承过来的成员,如果原来是某类的友元,那么它作为派生类的成员仍然是某类的友元。总之:元,那么它作为派生类的成员仍然是某类的友元。总之:n(1 1)基类的友元不一定是派生类的友元;)基类的友元不一定是派生类的友元;n(2 2)基类的成员是某类的友元,则其作为派生类继承的成)基类的成员是某类的友元,则其作为派生类继承
47、的成员仍是某类的友元。员仍是某类的友元。528.3 8.3 其他特征的继承关系其他特征的继承关系 2 2静态成员的继承静态成员的继承如果基类中被派生类继承的成员是静态成员,如果基类中被派生类继承的成员是静态成员,则其静态属性也随静态成员被继承过来。则其静态属性也随静态成员被继承过来。具体地说,如果基类的静态成员是公有的或是具体地说,如果基类的静态成员是公有的或是保护的,则它们被其派生类继承为派生类的静保护的,则它们被其派生类继承为派生类的静态成员。即:态成员。即:(1 1)这些成员通常用)这些成员通常用“:”方式引用或调用。方式引用或调用。(2 2)这些成员无论有多少个对象被创建,都只)这些成
48、员无论有多少个对象被创建,都只有一个拷贝。它为基类和派生类的所有对象所有一个拷贝。它为基类和派生类的所有对象所共享。共享。53 8.3.2 8.3.2 与基类对象和派生类对象相关的赋与基类对象和派生类对象相关的赋值兼容问题值兼容问题派生类对象间的赋值操作依据下面的原则:派生类对象间的赋值操作依据下面的原则:n(1 1)如果派生类有自己的赋值运算符的重载定义,)如果派生类有自己的赋值运算符的重载定义,即按该重载函数处理。即按该重载函数处理。n(2 2)派生类未定义自己的赋值操作,而基类定义了)派生类未定义自己的赋值操作,而基类定义了赋值操作,则系统自动定义派生类赋值操作,其中基赋值操作,则系统自
49、动定义派生类赋值操作,其中基类成员的赋值按基类的赋值操作进行。类成员的赋值按基类的赋值操作进行。n(3 3)二者都未定义专门的赋值操作,系统自动定义)二者都未定义专门的赋值操作,系统自动定义缺省赋值操作(按位进行拷贝)。缺省赋值操作(按位进行拷贝)。54 另一方面,基类对象和派生类对象之间允许有下另一方面,基类对象和派生类对象之间允许有下述的赋值关系(允许将派生类对象述的赋值关系(允许将派生类对象“当作当作”基类基类对象来使用):对象来使用):(1 1)基类对象基类对象 =派生类对象派生类对象;只赋只赋“共性成员共性成员”部分部分 ,反方向的下述赋值不被,反方向的下述赋值不被允许允许 (2 2
50、)指向基类对象的指针指向基类对象的指针 =派生类对象的地址派生类对象的地址;下述赋值不允许:指向派生类类型的指针下述赋值不允许:指向派生类类型的指针 =基类基类对象的地址。对象的地址。注:访问非基类成员部分时,要经注:访问非基类成员部分时,要经过指针类型的强制转换过指针类型的强制转换 (3 3)基类的引用基类的引用 =派生类对象派生类对象;下述赋值不允许:派生类的引用下述赋值不允许:派生类的引用 =基类对象。基类对象。注:通过引用只可以访问基类成员部分注:通过引用只可以访问基类成员部分55#include class base/基类基类base int a;public:base(int sa