《C语言继承与派生.ppt》由会员分享,可在线阅读,更多相关《C语言继承与派生.ppt(99页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第7章继承与派生第第7章章继承与派生继承与派生7.1继承与派生继承与派生7.2多继承多继承7.3类的继承方式类的继承方式7.4派生类的构造和析构函数派生类的构造和析构函数7.5派生中成员的标识与访问派生中成员的标识与访问7.6虚基类虚基类7.7类模板的派生和继承类模板的派生和继承第7章继承与派生7.1继承与派生继承与派生7.1.1继承与派生的概念 举个简单的例子:举个简单的例子:“狗狗”和和“黑狗黑狗”。当谈论。当谈论“狗狗”的时的时候,知道它是哺乳动物,有候,知道它是哺乳动物,有4 4条腿,条腿,1 1条尾巴,喜欢啃肉骨头,条尾巴,喜欢啃肉骨头,。现在谈论。现在谈论“黑狗黑狗”,人们会怎么说
2、呢?当然可以说:,人们会怎么说呢?当然可以说:“黑狗是一种哺乳动物,有黑狗是一种哺乳动物,有4 4条腿,条腿,1 1条尾巴,喜欢吃肉骨头,条尾巴,喜欢吃肉骨头,并且它的毛是黑色的,并且它的毛是黑色的”。但是人们一般都不这么说,而。但是人们一般都不这么说,而是说:是说:“黑狗就是黑毛的狗黑狗就是黑毛的狗”。比较一下这两种说法,显然后。比较一下这两种说法,显然后一种说法更好。那么它好在哪里呢?第一,它更简炼;第二,一种说法更好。那么它好在哪里呢?第一,它更简炼;第二,更重要的是它反映了更重要的是它反映了“狗狗”和和“黑狗黑狗”这两个概念的内在联系。这两个概念的内在联系。“狗狗”和和“黑狗黑狗”之间
3、存在一条重要的联系,那就是所有的之间存在一条重要的联系,那就是所有的“黑狗黑狗”都是都是“狗狗”,或者说,或者说,“黑狗黑狗”是一类特殊的是一类特殊的“狗狗”。根据这一条,根据这一条,“狗狗”所具有的特征,例如所具有的特征,例如4 4条腿,条腿,1 1条尾巴等,条尾巴等,“黑狗黑狗”自然都具有。也就是说,自然都具有。也就是说,“黑狗黑狗”从从“狗狗”那里继承那里继承了了“狗狗”的全部特征。的全部特征。第7章继承与派生所所谓谓继继承承,就就是是新新的的类类从从已已有有类类那那里里得得到到已已有有的的特特性性。从另一个角度来看,从已有类产生新类的过程就是类的派生。已已有有的的类类称称为为基基类类或
4、或父父类类,产产生生的的新新类类称称为为派派生生类类或或子子类类。派生类同样也可以作为基类再派生新的类,这样就形成了类的层次结构。类的继承和派生的层次结构,可以说是人们对自然界中的事物进行分类、分析和认识的过程在程序设计中的体现。现实世界中的事物都是相互联系、相互作用的,人们在认识过程中,根据事物的实际特征,抓住其共同特性和细小差别,利用分类的方法进行分析和描述。C+C+中中有有两两种种继继承承:单单一一继继承承和和多多重重继继承承。对对于于单单一一继继承承,派派生生类类只只能能有有一一个个基基类类;对对于于多多重重继继承承,派派生生类类可可以以有多个基类。有多个基类。第7章继承与派生图7-1
5、交通工具分类层次图例如,对于交通工具的分类见图7-1。第7章继承与派生7.1.2单一继承的派生类声明在C+中,派生类的一般声明语法如下:class:继承方式派生类成员声明;其中:class是类声明的关键字,用于告诉编译器下面声明的是一个类。派生类名是新生成的类名。第7章继承与派生继承方式规定了如何访问从基类继承的成员。继承方式关键字为private、public和protected,分别表示私有继承、公有继承和保护继承。如果不显式地给出继承方式关键字,系统的默认值就认为是私有继承(private)。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限,这将在7.3节中详细介
6、绍。派生类成员指除了从基类继承来的所有成员之外,新增加的数据和函数成员。这些新增的成员正是派生类不同于基类的关键所在,是派生类对基类的发展。当重用和扩充已有的代码时,就是通过在派生类中新增成员来添加新的属性和功能。可以说,这就是类在继承基础上的进化和发展。第7章继承与派生例如,从基类vehicle(汽车)公有派生car(小汽车)类的声明形式如下:classvehicle/基类vehicle类的声明private:/私有数据成员intwheels;floatweight;public:/公有函数成员voidinitvehicle(intin_wheels,floatin_weight);intg
7、et_wheels();floatget_weight();/.;第7章继承与派生classcar:publicvehicle/派生类car类的声明private:/新增私有数据成员intpassenger_load;public:/新增公有函数成员voidinitcar(intin_wheels,floatin_weight,intpeople=4);intget_passengers();/.;第7章继承与派生7.1.3派生类生成过程仔细分析派生新类这个过程,实际是经历了三个步骤:吸吸收基类成员,改造基类成员和添加新的成员。收基类成员,改造基类成员和添加新的成员。面向对象的继承和派生机制,
8、其最主要的目的是实现代码的重用和扩充重用和扩充。因此,吸收基类成员就是一个重用的过程,而对基类成员进行调整、改造以及添加新成员就是原有代码的扩充过程,二者是相辅相成的。下面以某公司人员管理系统为例,分别对这几个步骤进行解释。基类employee和派生类technician声明如下,类的实现部分略去。第7章继承与派生classemployeeprotected:char*name;/姓名intindividualEmpNo;/个人编号intgrade;/级别floataccumPay;/月薪总额staticintemployeeNo;/本公司职员编号目前最大值public:employee();
9、employee();voidpay();/计算月薪函数voidpromote(int);/升级函数voiddisplayStatus();/显示人员信息;第7章继承与派生classtechnician:publicemployeeprivate:floathourlyRate;/每小时酬金intworkHours;/当月工作时数public:technician();/构造函数voidpay();/计算月薪函数voiddisplayStatus();/显示人员信息;第7章继承与派生1吸收基类成员在类继承中,第一步是将基类的成员全盘接收,这样派生类实际上就包含了它的所有基类中除构造和析构函数之
10、外的所有成员。注意,在派生过程中,构造函数和析注意,在派生过程中,构造函数和析构函数都不被继承。构函数都不被继承。这里派生类technician继承了基类employee中除构造和析构函数之外的所有成员:name,individualEmpNo,grade,accumPay,employeeNo,pay(),promote(int),displayStatus()。经过派生过程,这些成员便存在于派生类之中。第7章继承与派生2改造基类成员第一是基类成员的访问控制,主要依靠派生类声明时的继承方式来控制。第二是对基类数据或函数成员的覆盖,就是在派生类中声明一个和基类数据或函数同名的成员,例如,上例中
11、的pay()和displayStatus()。如果派生类声明了一个和某个基类成员同名的新成员(如果是成员函数,则参数表也要相同,参数不同的情况属于重载),派生的新成员就覆盖了外层同名成员。这时,在派生类中或者通过派生类的对象直接使用成员名就只能访问到派生类中声明的同名成员,这称为同名覆盖。在上例的程序中,派生类technician中的pay()和displayStatus()函数就覆盖了基类employee中的同名函数第7章继承与派生3添加新的成员派生类新成员的加入是继承与派生机制的核心,是保证派生类在功能上有所发展的关键。我们可以根据实际情况的需要,给派生类添加适当的数据和函数成员,以实现必
12、要的新增功能。这里派生类technician中就添加了数据成员hourlyRate和workHours。第7章继承与派生7.2多继承多继承7.2.1多继承的声明在派生类的声明中,基类名可以有一个,也可以有多个。如果只有一个基类名,则这种继承方式称为单继承;如果基类名有多个,则这种继承方式称为多继承,这时的派生类同时得到了多个已有类的特征。在多继承中,各个基类名之间用逗号隔开。多继承的声明语法如下:class:继承方式基类名1,继承方式基类名2,.,继承方式基类名n派生类成员声明;第7章继承与派生例如,假设基类Basel、Base2是已经声明的类,下面的语句声明了一个名为MultiDerived
13、的派生类,该类从基类Basel、Base2派生而来。classBase1/.;classBase2/.;classMultiDerived:publicBase1,privateBase2public:MultiDerived();MultiDerived();/.;第7章继承与派生图7-2多继承和单继承第7章继承与派生7.2.2类族一个基类可以同时派生出多个派生类。也就是说,一个类从父类继承来的特征也可以被其它新的类所继承,一个父类的特征,可以同时被多个子类继承。这样就形成了一个相互关联的类的家族,称为类族类族。在类族中,直接参与派生出某类的基类称为直接基类;基类的基类甚至更高层的基类称为间
14、接基类。图7-3所示为一个单继承的多层类族,其中A类派生出B类,B类又派生出E类,则B类是E类的直接基类,A类是B类的直接基类,而A类可以称为E类的间接基类。第7章继承与派生图7-3单继承类族示意图第7章继承与派生7.3类的继承方式类的继承方式在面向对象程序中,基类的成员可以有public(公有)、protected(保护)和private(私有)三种访问属性。派派生生类类继继承承了了基基类类的的全全部部数数据据成成员员和和除除了了构构造造、析析构构函函数数之之外外的的全全部部函函数数成成员员,但但是是这这些些成成员员的的访访问问属属性性在在派派生生的的过过程程中中是是可可以以调调整整的的。从
15、基类继承的成员,其访问属性由继承方式控制。类的继承方式有public(公有)继承、protected(保护)继承和private(私有)继承三种。对于不同的继承方式,会导致基类成员原来的访问属性在派生类中有所变化。表7-1列出了不同继承方式下基类成员各访问属性的变化情况(/test_inherit.cpp)第7章继承与派生表7-1访问属性与继承的关系第7章继承与派生7.3.1公有继承当类的继承方式为public(公有)继承时,基类的public(公有)和protected(保护)成员的访问属性在派生类中不变,而基类的private(私有)成员仍保持私有属性。也就是说,派生类的其它成员可以直接访
16、问基类的公有成员和保护成员。其它外部使用者只能通过派生类的对象访问继承来的公有成员。而而无无论论是是派派生生类类的的成成员员还还是是派派生生类类的的对对象象,都都无无法法访访问问基基类类的私有成员。的私有成员。第7章继承与派生【例7-1】公有继承例题。/test_inherit_public.cpp从基类vehicle(汽车)公有派生car(小汽车)类,car类继承了vehicle类的全部特征,同时,car类自身也有一些特点,这就需要在继承vehicle类的同时添加新的成员。#includeclassvehicle/基类vehicle类的声明private:/私有数据成员intwheels;f
17、loatweight;public:/公有函数成员vehicle(intin_wheels,floatin_weight)wheels=in_wheels;weight=in_weight;intget_wheels()returnwheels;floatget_weight()returnweight;第7章继承与派生classcar:publicvehicle/派生类car类的声明private:/新增私有数据成员intpassenger_load;public:/新增公有函数成员car(intin_wheels,floatin_weight,intpeople=5):vehicle(in
18、_wheels,in_weight)passenger_load=people;intget_passengers()returnpassenger_load;voidmain()carbluebird(4,1000);/carobjectcoutbluebird.get_wheels()endl;/输出小汽车的信息coutbluebird.get_weight()endl;coutbluebird.get_passenger()endl;第7章继承与派生7.3.2私有继承/test_inherit_private.cpp当类的继承方式为private(私有)继承时,基类中的public(公有
19、)成员和protected(保护)成员都以私有成员身份出现在派生类中,而基类的private(私有)成员在派生类中不可访问。也就是说,基类的基类的public成员和成员和protected成员被继承后作为派生类的成员被继承后作为派生类的private成员,派生类的其它成成员,派生类的其它成员可以直接访问它们,但是在类外部通过派生类的对象无员可以直接访问它们,但是在类外部通过派生类的对象无法访问。法访问。特别要注意,基类的private成员仍保持private属性,这样,无论是派生类的成员还是通过派生类的对象,都无法访问从基类继承的私有成员。第7章继承与派生#includeclassvehicl
20、e/基类vehicle类的声明private:/私有数据成员intwheels;floatweight;public:/公有函数成员vehicle(intin_wheels,floatin_weight)wheels=in_wheels;weight=in_weight;intget_wheels()returnwheels;floatget_weight()returnweight;【例7-2】私有继承例题。第7章继承与派生classcar:privatevehicle/派生类car类的声明private:/新增私有数据成员intpassenger_load;public:/新增公有函数成员
21、car(intin_wheels,floatin_weight,intpeople=5):vehicle(in_wheels,in_weight)passenger_load=people;intget_wheels()returnvehicle:get_wheels();/重新定义get_wheels()floatget_weight()returnvehicle:get_weight();/重新定义get_weight()intget_passengers()returnpassenger_load;第7章继承与派生voidmain()carbluebird(4,1000);/声明car类
22、的对象coutThemessageofbluebird(wheels,weight,passengers):endl;coutbluebird.get_wheels(),/输出小汽车的信息bluebird.get_weight(),bluebird.get_passengers()endl;第7章继承与派生继承方式为私有继承。这时,基类中的公有和保护成员在派生类中都以私有成员的身份出现。派生类的成员函数及对象无法访问基类的私有数据(例如基类的wheels和weight)。派生类的成员仍然可以访问到从基类继承过来的公有和保护成员,但是在类外部通过派生类的对象根本无法访问到基类的任何成员,基类原有
23、的外部接口(例如基类的get_wheels()和get_weight()函数)被派生类封装和隐蔽起来。当然,派生类新增的成员之间仍然可以自由地互相访问。在私有继承情况下,为了保证基类的部分外部接口特征能够在私有继承情况下,为了保证基类的部分外部接口特征能够在派生类中也存在,就必须在派生类中重新定义同名的成员函数。在派生类中也存在,就必须在派生类中重新定义同名的成员函数。第7章继承与派生7.3.3保护继承保护继承中,基类的public(公有)和protected(保护)成员都以保护成员的身份出现在派生类中,而基类的private(私有)成员不可访问。具体说,基类中的保护成员只能被基类的成员函数或
24、派生类的成员函数访问,不能被派生类以外的成员函数访问。第7章继承与派生#includeclassvehicle/基类vehicle类的声明private:/私有数据成员intwheels;protected:/保护数据成员floatweight;public:/公有函数成员vehicle(intin_wheels,floatin_weight)wheels=in_wheels;weight=in_weight;intget_wheels()returnwheels;floatget_weight()returnweight;【例7-3】保护继承例题。/test_inherit_protecte
25、d.cpp第7章继承与派生classcar:protectedvehicle/派生类car类的声明private:/新增私有数据成员intpassenger_load;public:/新增公有函数成员car(intin_wheels,floatin_weight,intpeople=5):vehicle(in_wheels,in_weight)passenger_load=people;intget_wheels()returnvehicle:get_wheels();/重新定义get_wheels()floatget_weight()returnvehicle:get_weight();/重
26、新定义get_weight()intget_passengers()returnpassenger_load;第7章继承与派生voidmain()carbluebird(4,1000);/声明car类的对象coutThemessageofbluebird(wheels,weight,passengers):endl;coutbluebird.get_wheels(),/输出小汽车的信息bluebird.get_weight(),bluebird.get_passengers()endl;第7章继承与派生在例7-3中,我们将例7-2中私有数据成员weight改为保护数据成员,类定义的其它部分没改
27、变,继承方式改为保护继承。这时,基类中的公有和保护成员在派生类中都以保护成员的身份出现。派生类的成员函数及对象无法访问基类的私有数据和保护数据(例如基类的wheels和weight)。派生类的成员仍然可以访问到从基类继承过来的公有和保护成员。同私有继承一样,在保护继承情况下,为了保证基类的部分外部接口特征能够在派生类中也存在,就必须在派生类中重新定义同名的成员函数。这里在派生类car中,重新定义了get_wheels()和get_weight()函数。根据同名覆盖的原则,在主函数中自然调用的是派生类的函数。第7章继承与派生可以看出在直接派生类中,所有成员的访问属性都是完全相同的。但是,如果派生
28、类作为新的基类继续派生时,二者的区别就出现了。如图7-4所示。图中7-4(a)说明B类以私有方式继承了A类后,又派生出C类,则C类的成员和对象都不能访问间接从A类中继承来的成员。图7-4(b)说明B类以保护方式继承了A类,那么A类中的公有和保护成员在B类中都是保护成员。第7章继承与派生图7-4类的保护成员的访问规则(a)私有继承(b)保护继承第7章继承与派生7.4派生类的构造和析构函数派生类的构造和析构函数7.4.1构造函数派生类对象的初始化也是通过派生类的构造函数实现的。具体来说,就是对该类的数据成员赋初值。派生类的数据成员由所有基类的数据成员与派生类新增的数据成员共同组成,如果派生类新增成
29、员中包括有内嵌的其它类对象,派生类的数据成员中实际上还间接包括了这些对象的数据成员。因此,初始化派生类的对象时,就要对基类数据成员、新增数初始化派生类的对象时,就要对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。据成员和成员对象的数据成员进行初始化。因此,派生类的构造函数需要以合适的初值作为参数,隐含调用基类和新增的内嵌对象成员的构造函数来初始化它们各自的数据成员,然后再加入新的语句对新增普通数据成员进行初始化。第7章继承与派生派生类构造函数声明的一般语法形式如下::(参数总表):基类名1(参数表1),.,基类名n(参数表n),内嵌对象名1(内嵌对象参数表1),.,内嵌对象名m(内
30、嵌对象参数表m)派生类新增成员的初始化语句;其中:其中:派生类的构造函数名与派生类名相同。参数总表需要列出初始化基类数据、新增内嵌对象数据及新增一般成员数据所需要的全部参数。冒号之后,列出需要使用参数进行初始化的基类名和内嵌成员名及各自的参数表,各项之间用逗号分隔。第7章继承与派生#includeclassvehicle/基类vehicle的声明private:intwheels;floatweight;public:/基类vehicle的构造函数vehicle(intin_wheels,floatin_weight)wheels=in_wheels;weight=in_weight;/.;派
31、生类构造函数的执行顺序一般是,先祖先派生类构造函数的执行顺序一般是,先祖先(基类基类),再客人,再客人(内嵌对内嵌对象象),后自己,后自己(派生类本身派生类本身)。例:。例:第7章继承与派生classcar:publicvehicle/派生类car的声明private:/新增私有数据成员intpassenger_load;public:/派生类car的构造函数car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight)passenger_load=people;/.;voidmain()carbluebird
32、(4,3);/声明派生类car的对象/.第7章继承与派生7.4.2析构函数在派生过程中,基类的析构函数不能继承,如果需要析构函数的话,就要在派生类中重新定义。析构函数没有类型,也没有参数,和构造函数相比,情况略为简单。派生类析构函数派生类析构函数的定义方法与没有继承关系的类中析构函数的定义方法完全相同,只要在函数体中负责把派生类新增的非对象成员的清理工作做好就够了,系统会自己调用基类及成员对象的析构函数来对基类及对象成员进行清理。但它的执行顺序和构但它的执行顺序和构造函数正好严格相反造函数正好严格相反先自己先自己(派生类本身派生类本身),再客人,再客人(内嵌对象内嵌对象),后祖先,后祖先(基类
33、基类)。第7章继承与派生【例7-4】派生类的构造函数和析构函数(多继承,含有内嵌对象)#include/classBase1/基类Base1,构造函数有参数public:Base1(inti)coutconstructingBase1iendl;Base1()coutdestructingBase1endl;/Base1的析构函数;classBase2/基类Base2,构造函数有参数public:Base2(intj)coutconstructingBase2jendl;Base2()coutdestructingBase2endl;/Base2的析构函数;classBase3/基类Base3
34、,构造函数无参数public:Base3()coutconstructingBase3endl;Base3()coutdestructingBase3endl;/Base3的析构函数;第7章继承与派生classDerive:publicBase2,publicBase1,publicBase3/派生新类private:/派生类新增私有对象成员Base1memberBase1;Base2memberBase2;Base3memberBase3;public:/派生类的构造函数Derive(inta,intb,intc,intd):Base2(b),memberBase2(d),memberBas
35、e1(c),Base1(a);voidmain()Deriveobject(2,4,6,8);第7章继承与派生考虑Derive类的构造函数的执行情况。它它应应该该是是先先调调用用基基类类的的构构造造函函数数,然然后后调调用用内内嵌嵌对对象象的的构构造造函函数数。基基类类构构造造函函数数的的调调用用顺顺序序是是按按照照派派生生类类声声明明时时的的顺顺序序,因此,应该是先Base2,再Base1,再Base3。而内嵌对象的构造函数的调用顺序应该是按照成员在类中声明的顺序,应该是先Base1,再Base2,再Base3。程序运行的结果也完全证实了这种分析。程序运行结果为constructingBas
36、e24constructingBase12constructingBase3constructingBase16constructingBase28constructingBase3destructingBase3destructingBase2destructingBase1destructingBase3destructingBase1destructingBase2第7章继承与派生7.5派生中成员的标识与访问派生中成员的标识与访问7.5.1作用域分辨在派生类的访问中,有两个问题需要解决:第一是唯一标识问题;第二是可见性问题。对于在不同的作用域声明的标识符,其可见性原则是:如果存在两个或多
37、个具有包含关系的作用域,并且外层声明的标识符如果在内层没有声明同名的标识符,那么它在内层仍可见;如果内层声明了同名的标识符,则外层标识符在内层不可见,这时称内层变量覆盖了外层同名变量,这种现象称为同名覆盖。1作用域分辨符作用域分辨符就是我们经常见到的“:”,它可以用来限定要访问的成员归属哪个类,其一般的使用形式如下::(参数表)在类的派生层次结构中,基类的成员和派生类新增的成员都具有类作用域。二者的作用范围不同,是相互包含的两个层,派生类在内层。第7章继承与派生【例7-5】继承中使用作用域分辨符例题。#includeclassBase/声明基类Basepublic:intn;voidfun()
38、coutThisisBase,n=nendl;classDerive:publicBase/声明派生类Derivepublic:intn;/同名数据成员voidfun()coutThisisDerive,n=nendl;/同名函数成员;第7章继承与派生voidmain()Deriveobj;obj.n=1;/对象名.成员名标识obj.fun();/对象名.成员函数名标识obj.Base:n=2;/作用域分辨符标识obj.Base:fun();/访问Base基类成员程序运行结果为ThisisDerive,n=1ThisisBase,n=2第7章继承与派生2.多继承中作用域分辨符的使用在多继承中,
39、如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称。因此,派生类中也就会产生同名现象。对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须用直接基类来进行限定。我们再来看一个例题。第7章继承与派生【例7-6】多继承中使用作用域分辨符例题。类的派生关系及派生类的结构见图7-5。图7-5多重继承情况下派生类Level3继承关系、成员构成图(a)继承关系(b)Level3类结构intLevel22:n1第7章继承与派生现在我们来讨论同名成员n1和fun1()的标识与访问问题。间接基类Level1的成员经过两次派生之后,通
40、过不同的派生路径以相同的名字出现在派生类Level3中。这时,如果使用基类名Level1来限定,同样无法表明成员到底是从Level21还是Level22继承过来的,因此,必须使用直接基类Level21或者Level22的名称来限定,才能够唯一标识和访问成员。/test_derive1.cpp程序代码如下:#includeclassLevel1/声明基类Level1public:intn1;voidfun1()coutThisisLevel1,n1=n1endl;第7章继承与派生classLevel21:publicLevel1/声明派生类Level21public:intn21;classLe
41、vel22:publicLevel1/声明派生类Level22public:intn22;classLevel3:publicLevel21,publicLevel22/声明派生类Level3public:intn3;voidfun3()coutThisisLevel3,n3=n3endl;第7章继承与派生voidmain()Level3obj;obj.n3=1;obj.fun3();obj.Level21:n1=2;/使用直接基类obj.Level21:fun1();/使用直接基类obj.Level22:n1=3;/使用直接基类obj.Level22:fun1();/使用直接基类在程序主函数
42、中,创建了一个派生类的对象obj。如果只通过成员名称来访问该类的成员n1和fun1(),系统就无法唯一确定要引用的成员。这时,必须使用作用域分辨符,通过直接基类名来确定要访问的从基类继承来的成员。程序运行结果为ThisisLevel3,n3=1ThisisLevel1,n1=2ThisisLevel1,n1=3第7章继承与派生这种情况下,派派生生类类对对象象在在内内存存中中就就同同时时拥拥有有成成员员n1及及fun1()的的两两份份同同名名拷拷贝贝。对于数据成员来讲,两个n1可以分别通过Level21和Level22调用Level1的构造函数进行初始化,可以存放不同的数值,也可以使用作用域分辨
43、符通过直接基类名的限定来分别进行访问。但但是是在在很很多多情情况况下下,我我们们只只需需要要一一个个这这样样的的数数据据拷拷贝贝,同同一一成成员员的的多多份份拷拷贝贝增增加加了了内内存存的的开开销销。C+提提供供了了虚虚基基类类技技术来解决这一问题,术来解决这一问题,这部分内容将在7.6节中介绍。第7章继承与派生7.5.2基类私有成员的访问不管是私有派生还是公有派生,派生类都无权访问基类的私有成员。派生类想要使用基类的私有成员,只能通过调用基类的成员函数来实现,也就是使用基类所提供的接口。对于需要频繁访问基类私有成员的派生类来说,这种方式使用起来非常不方便。因此,需要寻求直接访问基类私有成员的
44、方式。有两种方式可供选择,下面分别介绍。1.在类定义体中增加保护段保护段成员可以被它的派生类访问,但是对于外界是隐藏的。这样,即方便了派生类的访问,又禁止了外界对它的操作。例:第7章继承与派生程序代码如下:#includeclassvehicle/基类vehicle类的声明protected:/保护数据成员intwheels;floatweight;public:/公有函数成员vehicle(intin_wheels,floatin_weight)wheels=in_wheels;weight=in_weight;intget_wheels()returnwheels;floatget_wei
45、ght()returnweight;floatwheel_load()returnweight/wheels;voidprint();第7章继承与派生classcar:vehicle/派生类car类的声明private:/新增私有数据成员intpassenger_load;public:/新增公有函数成员car(intin_wheels,floatin_weight,intpeople=4):vehicle(in_wheels,in_weight)passenger_load=people;intget_passengers()returnpassenger_load;voidprint();
46、第7章继承与派生classstruck:vehicle/派生类truck类的声明private:/新增私有数据成员intpassenger_load;floatpayload;public:/新增公有函数成员truck(intin_wheels,floatin_weight,intpeople=2,floatmax_load=24000.00):vehicle(in_wheels,in_weight)passenger_load=people;payload=max_load;intget_passengers()returnpassenger_load;floatefficiency()re
47、turnpayload/(payload+weight);voidprint();第7章继承与派生voidvehicle:print()/输出汽车类vehicle的数据coutthewheelsofvehicleiswheelsendl;couttheweightofvehicleisweightendl;coutendl;voidcar:print()/输出小车类输出小车类car的数据的数据coutthewheelsofcariswheelsendl;couttheweightofcarisweightendl;coutthepassenger_loadofcarispassenger_lo
48、adendl;coutendl;voidtruck:print()/输出卡车类输出卡车类truck的数据的数据coutthewheelsoftruckiswheelsendl;couttheweightoftruckisweightendl;coutthepassenger_loadoftruckispassenger_loadendl;couttheefficencyoftruckisefficiency()endl;coutendl;第7章继承与派生voidmain()carbluebird(4,1000,5);/声明car类的对象truckdongfeng(10,5000,3,34000
49、);/声明truck类的对象bluebird.print();dongfeng.print();程序运行结果为Thewheelsofcaris4Theweightofcaris1000Thepassenger_loadofcaris5Thewheelsoftruckis10Theweightoftruckis5000Thepassenger_loadoftruckis3Theefficiencyoftruckis0.871795第7章继承与派生2将需访问基类私有成员的派生类成员函数声明为基类的友元另一种访问基类私有成员的方法是将待访问的成员函数声明为基类的友元,这样,派生类中的其它成员函数都无
50、权访问它,外界更不可能通过派生新类来访问基类的私有成员。关于友元函数在前面第5章已介绍过,这里就不再赘述。第7章继承与派生#include#includeclassstringprivate:char*name;intlength;public:string(char*str)length=strlen(str);name=newcharlength+1;strcpy(name,str);voidshow()coutnameendl;7.5.3引入派生类后的对象指针【例7-8】引入派生类后的对象指针例题。第7章继承与派生classde_string:publicstringprivate:in