版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、16.116.1继承的概念继承的概念16.216.2继承的工作方式继承的工作方式16.316.3派生类的构造派生类的构造16.416.4继承与组合继承与组合16.516.5多态性多态性16.616.6多态的思考方式多态的思考方式16.716.7多态性如何工作多态性如何工作16.816.8不恰当的虚函数不恰当的虚函数16.916.9虚函数的限制虚函数的限制16.1016.10类的冗余类的冗余16.1116.11克服冗余带来的问题克服冗余带来的问题16.1216.12类的分解类的分解16.1316.13抽象类抽象类16.1416.14由抽象类派生具体类由抽象类派生具体类16.1516.15纯虚函数
2、的需要性纯虚函数的需要性17.117.1多继承如何工作多继承如何工作17.217.2继承的模糊性继承的模糊性17.317.3虚拟继承虚拟继承17.417.4多继承的构造顺序多继承的构造顺序17.517.5继承的访问控制继承的访问控制17.617.6保护继承与私有继承保护继承与私有继承1u继承(继承(inheritanceinheritance): : 一个新类从已有类那里获得其已有特性一个新类从已有类那里获得其已有特性( (属性、行为属性、行为) )u派生(派生(derivationderivation):):在已有类的基础上新增自己的特性而产生新类的过程在已有类的基础上新增自己的特性而产生新
3、类的过程2基类和派生类基类和派生类u 若一个类若一个类C2从另一个类从另一个类C1扩展而来扩展而来,则称则称C2为派生类(为派生类(derived class),),C1为基类(为基类(base class)。)。u 基类也称为父类(基类也称为父类(parent class), 派生类也称为扩展类(派生类也称为扩展类(extended class)或子类()或子类(child class)。)。u 一个派生类继承了其基类的所有数据成员和成员函数(构造函数一个派生类继承了其基类的所有数据成员和成员函数(构造函数和析构函数除外),还可以增加新的数据成员和函数,从而使派和析构函数除外),还可以增加新
4、的数据成员和函数,从而使派生类更具特殊化。生类更具特殊化。u 用指向基类的箭头表示两个类之间的继承关系。用指向基类的箭头表示两个类之间的继承关系。C1C1C2C2C3C3C1C1为基类为基类C2C2、C3C3为为C1C1的派生类的派生类345交通工具交通工具火车火车汽车汽车飞机飞机轮船轮船卡车卡车旅行车旅行车小汽车小汽车工具车工具车轿车轿车面包车面包车交通工具分类层次图交通工具分类层次图( (单继承单继承) )6计算机系人员计算机系人员教职工教职工教务人员教务人员学生学生教师教师行政管理人员行政管理人员本科生本科生系主任系主任研究生研究生计算机系组成人员的继承关系层次图(多继承)计算机系组成人
5、员的继承关系层次图(多继承)7继承的工作方式继承的工作方式举例举例 (ch16_1.cpp,p350)(ch16_1.cpp,p350)声明一个派生类,采用不同派生类型,从基类继承而来的成员的声明一个派生类,采用不同派生类型,从基类继承而来的成员的访问权限在派生类中也有所不同。访问权限在派生类中也有所不同。8l 派生类的继承方式为公有时,基类各成员在派生类中的访问权限:派生类的继承方式为公有时,基类各成员在派生类中的访问权限: 公有成员:公有成员:仍为派生类的公有成员仍为派生类的公有成员 保护成员:保护成员:仍为派生类的保护成员仍为派生类的保护成员 私有成员:私有成员:派生类不能访问,为基类私
6、有派生类不能访问,为基类私有l 派生类的成员只能访问基类中的派生类的成员只能访问基类中的public/protectedpublic/protected成员,不能访问成员,不能访问privateprivate成员。成员。l 派生类的对象(在派生类外)只能访问基类中的派生类的对象(在派生类外)只能访问基类中的publicpublic成员。成员。l 公有继承基本保持了基类的访问属性,因此使用较多。公有继承基本保持了基类的访问属性,因此使用较多。910直接访问从基类继承来的保护成员直接访问从基类继承来的保护成员x;基类;基类的私有成员的私有成员y,只能通过接口函数访问。,只能通过接口函数访问。con
7、st表示该成员函数不表示该成员函数不会修改数据成员的值会修改数据成员的值l 类的派生方式为私有继承时类的派生方式为私有继承时 基类的所有基类的所有publicpublic和和protectedprotected的成员在派生类中的访问权的成员在派生类中的访问权限变为限变为privateprivate。即基类的公有成员和保护成员被派生类继即基类的公有成员和保护成员被派生类继承过来以后,成为了派生类的私有成员。承过来以后,成为了派生类的私有成员。 基类的私有成员仍为基类的私有成员仍为基类私有基类私有。l 派生类的成员只能访问基类中的派生类的成员只能访问基类中的public/protectedpubl
8、ic/protected成员,不能成员,不能访问访问privateprivate成员成员l 派生类的对象不能访问基类中的任何成员派生类的对象不能访问基类中的任何成员11121314l 当类的派生方式为保护继承时:当类的派生方式为保护继承时: 基类的所有公有基类的所有公有publicpublic和保护和保护protectedprotected的成员在派生类中的的成员在派生类中的访问权限变为访问权限变为protectedprotected 基类的私有成员仍为基类的私有成员仍为基类私有。基类私有。l 基类中的公有和保护成员被派生类继承过来,作为派生类中的保基类中的公有和保护成员被派生类继承过来,作为
9、派生类中的保护成员,基类的私有成员在派生类中不能直接使用。护成员,基类的私有成员在派生类中不能直接使用。l 派生类的成员也只能访问基类中的派生类的成员也只能访问基类中的public/protectedpublic/protected成员,而不成员,而不能访问能访问privateprivate成员。成员。l 派生类的对象不能访问基类中的任何成员。派生类的对象不能访问基类中的任何成员。1516u在单个类中,在单个类中,protectedprotected和和privateprivate没有什么区别。没有什么区别。u但在继承关系中,基类的但在继承关系中,基类的privateprivate成员不但对应
10、用程序隐藏,也对派成员不但对应用程序隐藏,也对派生类隐藏。而基类的生类隐藏。而基类的protectedprotected成员只对应用程序隐藏,不对派生类隐成员只对应用程序隐藏,不对派生类隐藏。藏。保护继承和私有继承的区别保护继承和私有继承的区别 当把派生类当作基类去派生其他类时会产生不同结果。当把派生类当作基类去派生其他类时会产生不同结果。l用私有继承方式派生出来的类,从基类派生出来的公有和保用私有继承方式派生出来的类,从基类派生出来的公有和保护成员被当作这个类的私有成员,如果这个类去派生其他类,护成员被当作这个类的私有成员,如果这个类去派生其他类,它派生出来的类的成员函数将无法访问这些私有成
11、员;它派生出来的类的成员函数将无法访问这些私有成员;l采用保护方式派生出来的类采用保护方式派生出来的类, ,从基类中继承的公有和保护成从基类中继承的公有和保护成员被当作这个类的保护成员,如果用这个类去派生其他类,员被当作这个类的保护成员,如果用这个类去派生其他类,它的派生类的成员函数将可以直接访问这些成员。它的派生类的成员函数将可以直接访问这些成员。171819综合举例综合举例 (p389p389,继承的访问控制),继承的访问控制)class Base public: int m1; protected: int m2; private: int m3; ;class PriClass : p
12、rivate Base public: void test( ) m1=1; /ok,将将m1据为据为private m2=2; /ok m3=3; /error,不可访问基类的私有成员不可访问基类的私有成员 ; 20Class DerivedFromPri : public PriClass public: void test( ) m1=1; /error m2=2; /error m3=3; /error ; int main( ) PriClass priObj: priObj.m1=1; /error priObj.m2=2; /error priObj.m3=3; /error c
13、lass Base public: int m1; protected: int m2; private: int m3; ;class ProClass : protected Base public: void test( ) m1=1; /ok,将将m1据为据为protected m2=2; /ok m3=3; /error,不可访问基类的私有成员不可访问基类的私有成员 ; 21Class DerivedFromPro : public ProClass public: void test( ) m1=1; /m1仍为仍为protected m2=2; / m2 m2仍为仍为protec
14、tedprotected m3=3; /error,不可访问,不可访问 ; int main( ) ProClass proObj: proObj.m1=1; /error proObj.m2=2; /error proObj.m3=3; /error class Base public: int m1; protected: int m2; private: int m3; ;class PubClass : public Base public: void test( ) m1=1; /ok, m1为为public m2=2; /ok,m2为为protected m3=3; /error,
15、不可访问基类的私有成员不可访问基类的私有成员 ; 22Class DerivedFromPub : public PubClass public: void test( ) m1=1; /m1仍为仍为public m2=2; / m2 m2仍为仍为protectedprotected m3=3; /error,不可访问,不可访问 ; int main( ) PubClass pubObj: pubpubObj.m1=1; pubpubObj.m2=2; /error pubpubObj.m3=3; /error 23继承中的赋值兼容规则继承中的赋值兼容规则现有一个学生类现有一个学生类Stude
16、ntStudent,要增加研究生类,要增加研究生类GraduateStudentGraduateStudent。由于研究生除了他自己特有的性质外,还具有学生的所有性由于研究生除了他自己特有的性质外,还具有学生的所有性质,所以我们用继承的方法来重用学生类。质,所以我们用继承的方法来重用学生类。class Studentclass Student / /;class GraduateStudentclass GraduateStudent: : publicpublic Student Student / /;24void fn(Student & s)void fn(Student &a
17、mp; s) /任何学生想要做的事情任何学生想要做的事情 int mainint main()() GraduateStudent gs GraduateStudent gs; fn fn(gsgs);); /fn()/fn()把把gsgs视为视为StudentStudent类对象类对象 GraduateStudentGraduateStudent对象尺寸对象尺寸thisthis指针指针GraduateStudentGraduateStudent的内存布局图的内存布局图l 赋值兼容规则:是指在需要基类对象的任何地方都可以使用赋值兼容规则:是指在需要基类对象的任何地方都可以使用公有派生类的对象公
18、有派生类的对象来替代。来替代。通过公有继承,派生类得到了基类通过公有继承,派生类得到了基类中除构造函数和析构函数之外的所有成员。这样公有派生类实中除构造函数和析构函数之外的所有成员。这样公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。赋值兼容规则中所指的替代包括以下情况:生类都可以解决。赋值兼容规则中所指的替代包括以下情况:派生类的对象可以赋值给基类对象派生类的对象可以赋值给基类对象派生类的对象可以初始化基类的引用派生类的对象可以初始化基类的引用派生类对象的地址可以赋给指向基类对象的指针派生类对象的地址可以
19、赋给指向基类对象的指针l 在替代之后,派生类对象就可以作为基类的对象使用,但只在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。能使用从基类继承的成员。25由于赋值兼容规则的引入,对于基类及其公有派生类的对象,可以使用由于赋值兼容规则的引入,对于基类及其公有派生类的对象,可以使用相同的函数统一进行处理,而没有必要为每一个类设计单独的模块,从相同的函数统一进行处理,而没有必要为每一个类设计单独的模块,从而提高程序开发效率。而提高程序开发效率。class B0class B0 public: public: void display() void display() co
20、ut“B0:display()”endl; cout“B0:display()”endl;class B1: public B0class B1: public B0 public: public: void display() void display() cout“B1:display()”endl; cout“B1:display()”endl;class D1: public B1class D1: public B1 public: public: void display() void display() cout“D1:display()”endl; cout“D1:displa
21、y()”display(); ptr-display(); void main()void main() B0 b0; B0 b0; B1 b1; B1 b1; D1 d1; D1 d1; B0 B0 * *p;p; p=&b0; p=&b0; fun(p); fun(p); p=&b1; p=&b1; fun(p); fun(p); p=&d1; p=&d1; fun(p); fun(p); 26每一个派生类型对应的是紧接其后给出的基类名,而且必须给每个基类指定每一个派生类型对应的是紧接其后给出的基类名,而且必须给每个基类指定一种派生类型,若缺
22、省,相应的基类则取私有派生类型,而不是和前一个基一种派生类型,若缺省,相应的基类则取私有派生类型,而不是和前一个基类取相同的派生类型。类取相同的派生类型。272829派生类不能访问派生类不能访问基类的私有成员基类的私有成员30例:class Vehicle /车辆类车辆类 /;class Motor /马达类马达类 /;class Car : public Vehicle public: Motor motor;31void vehicleFn( Vehicle & v);void motorFn( Motor & m);int main Car c; vehicleFn( c
23、 ) ; /ok motorFn( c ) ; /error,参数要求是马达参数要求是马达 motorFn( c.motor ) ; /ok;汽车包含有马达,拥有汽车包含有马达,拥有一辆汽车同时也拥有一一辆汽车同时也拥有一个马达个马达32l 创建对象时要调用构造函数,对象消亡时要调用析构函数。创建对象时要调用构造函数,对象消亡时要调用析构函数。l 派生类派生类不继承不继承基类的构造函数和析构函数。基类的构造函数和析构函数。l 在构造一个派生类时,完成其基类部分的构造由基类的构造函数在构造一个派生类时,完成其基类部分的构造由基类的构造函数去做。去做。l 在创建派生类对象时在创建派生类对象时,系统
24、要,系统要调用派生类的构造函数,调用派生类的构造函数,在执行派在执行派生类构造函数生类构造函数( (体体) )前,系统前,系统要先调用其基类的构造函数要先调用其基类的构造函数以实现对以实现对从基类继承的数据成员的初始化,再执行派生类构造函数体以实从基类继承的数据成员的初始化,再执行派生类构造函数体以实现新增成员的初始化。现新增成员的初始化。l 派生类对象消亡时,派生类对象消亡时,33l 派生类在构造时,即使可以直接访问基类的保护数据成员以初派生类在构造时,即使可以直接访问基类的保护数据成员以初始化它们,一般也不这么做,而是通过基类的接口函数去访问始化它们,一般也不这么做,而是通过基类的接口函数
25、去访问它们,初始化也是通过基类的构造函数。这样,一旦基类实现它们,初始化也是通过基类的构造函数。这样,一旦基类实现有错,只要不涉及接口,基类的修改不会影响派生类的操作。有错,只要不涉及接口,基类的修改不会影响派生类的操作。l 类与类之间,各做各的,以接口作沟通,即使基类与子类也不类与类之间,各做各的,以接口作沟通,即使基类与子类也不例外。例外。class A public: A( ); A( ); void setx(int a)x=a; void sety(int b)y=b; int getx( ) constreturn x; int gety( ) constreturn y; pro
26、tected: int x; private: int y;class B: public A public: B( ); B( ); void setz(int c)z=c; int getz()return z; int getsum( )return x+gety()+z; private: int z;34A:A( ):x(1),y(2) cout调用类调用类A的构造函数的构造函数endl;A:A( ) cout调用类调用类A的析构函数的析构函数endl;B:B( ):z(3) cout调用类调用类B的构造函数的构造函数endl;B:B( ) cout调用类调用类B的析构函数的析构函数
27、endl;int main() B b; coutX=b.getx( )tY=b.gety( ) tZ=b.getz( )endl; coutX+Y+Z=b.getsum( )endl; return 0;35派生类名:派生类名(参数总表)派生类名:派生类名(参数总表):基类基类1(参数表(参数表1),),基类,基类n(参(参数表数表n),对象成员),对象成员1(对象成员参数表(对象成员参数表1),),对象成员,对象成员n(对象成员参数(对象成员参数表表n) 派生类中新声明的数据成员初始化语句派生类中新声明的数据成员初始化语句u对象成员对象成员是指在派生类中新声明的数据成员,它是另外一个类的对
28、象,是指在派生类中新声明的数据成员,它是另外一个类的对象,必须在初始化列表中初始化。必须在初始化列表中初始化。u基类名和对象成员名之间的基类名和对象成员名之间的编写顺序可以是任意的编写顺序可以是任意的,且对于使用默认构,且对于使用默认构造函数的基类和对象成员,可以不列出基类名和对象成员名。造函数的基类和对象成员,可以不列出基类名和对象成员名。一般形式如下一般形式如下36参数总表包括参数总表包括: 基类数据成员、内嵌对象数据成基类数据成员、内嵌对象数据成员、其他数据成员所需的全部数据。员、其他数据成员所需的全部数据。class GraduateStudent : public Student 例
29、:ch16_1.cpp的扩展 public: GraduateStudent (char * pName, Advisor & adv) : Student(pName), advisor(adv) qualifierGrade=0; /其余见ch16_1.cpp;void fn (Advisor & advisor) GraduateStudent gs ( “Kate”, advisor); / int main( ) Advisor da; fn(da);37派生类的构造总是由基类的初派生类的构造总是由基类的初始化开始的,基类的初始化由始化开始的,基类的初始化由Stude
30、ntStudent(pNamepName)完成。用)完成。用advadv来初始化来初始化advisoradvisor对象成员。对象成员。调用基类调用基类StudentStudent的构造函数的构造函数调用调用AdvisorAdvisor的构造函数的构造函数最后调用最后调用GraduateStudentGraduateStudent的构造函数的构造函数析析构构class GraduateStudent : public Student public: GraduateStudent (char * pName, Advisor & adv) : advisor(adv), Student
31、(pName) qualifierGrade=0; /其余见ch16_1.cpp;void fn (Advisor & advisor) GraduateStudent gs ( “Kate”, advisor); / int main( ) Advisor da; fn(da);38派生类构造开始,仍先调用基类的构造函数派生类构造开始,仍先调用基类的构造函数Student(pName)Student(pName),若没有,若没有Student(pName)Student(pName),则派生类会调用基类的默认构造函数,若找则派生类会调用基类的默认构造函数,若找不到匹配的构造函数,就通
32、不过编译。不到匹配的构造函数,就通不过编译。调用基类调用基类StudentStudent的构造函数的构造函数调用调用AdvisorAdvisor的构造函数的构造函数最后调用最后调用GraduateStudentGraduateStudent的构造函数的构造函数析析构构39( 4041提示:提示:u 若要将某个类设计为基类,最好为它设计一个无参构造函数,以若要将某个类设计为基类,最好为它设计一个无参构造函数,以避免编程错误。避免编程错误。例:例:class Fruit public:Fruit(int id) ;class Apple : public Fruit public:Apple( )
33、 ;u 由于由于Apple是是Fruit的派生类,的派生类, Apple的构造函数会自动调用的构造函数会自动调用Fruit的无参构造函数,但是的无参构造函数,但是Fruit没有无实参构造函数,因此会产生没有无实参构造函数,因此会产生一个编译错误。一个编译错误。42434445 在派生类中,可以定义同基类中的成员同名的成员,此时:在派生类中,可以定义同基类中的成员同名的成员,此时:u在派生类中基类的同名成员被屏蔽。在派生类中基类的同名成员被屏蔽。u若在派生类中访问基类被屏蔽的同名成员,则若在派生类中访问基类被屏蔽的同名成员,则必须使用作必须使用作用域运算符用域运算符:以明确指定其所属的类,来消除
34、二义性。以明确指定其所属的类,来消除二义性。 基类名基类名:基类同名数据成员基类同名数据成员 基类名基类名:基类同名成员函数基类同名成员函数(实参列表实参列表)464748495051l 多态:多态:不同的对象接受到相同的消息时产生不同的响应动作,不同的对象接受到相同的消息时产生不同的响应动作,即对应相同的函数名,却执行了不同的函数体即对应相同的函数名,却执行了不同的函数体( (函数体是事先定函数体是事先定义好的义好的) )。l 多态性多态性(PolymorphismPolymorphism ,源于希腊语,含义为,源于希腊语,含义为“多种形式多种形式”) )是面向对象程序设计的关键技术之一。利
35、用多态性技术,可以是面向对象程序设计的关键技术之一。利用多态性技术,可以调用调用同一个函数名同一个函数名的函数,实现完全不同的功能。的函数,实现完全不同的功能。52C+C+允许子类的成员函数允许子类的成员函数重载重载基类的成员函数。基类的成员函数。53例如:例如:class Studentclass Student public: public: float calcTuition( )float calcTuition( ) / /计算学费计算学费 / / ;class GraduateStudent: public Studentclass GraduateStudent: public
36、Student public: public: float calcTuition( )float calcTuition( ) / / ;int main( )int main( ) Student s;Student s;GraduateStudent gs;GraduateStudent gs;s.calcTuition( ); s.calcTuition( ); /调用调用Student:calcTuition( );Student:calcTuition( );gs.calcTuition( ); gs.calcTuition( ); /调用调用GraduateStudent:cal
37、cTuition( );GraduateStudent:calcTuition( ); calcTuition( )calcTuition( )呈多态呈多态54先期联编(先期联编(early bindingearly binding):又称静态联编,是指编:又称静态联编,是指编译时就能确定哪个重载函数被调用。译时就能确定哪个重载函数被调用。迟后联编(迟后联编(late bindinglate binding): : 又称动态联编或滞后联又称动态联编或滞后联编,是指在程序运行时依据其类型确认调用哪个函数编,是指在程序运行时依据其类型确认调用哪个函数的能力。的能力。多态技术的优势:例,教材多态技术
38、的优势:例,教材p357p357从实现从实现的角度,的角度,C+中中的两种的两种多态性多态性 编译时的多态编译时的多态 运行时的多态运行时的多态 程序执行前,无法根据函数名和参数来程序执行前,无法根据函数名和参数来确定该调用哪个函数。必须在程序执行确定该调用哪个函数。必须在程序执行过程中,根据执行的具体情况来过程中,根据执行的具体情况来动态地动态地确定。通过类继承关系和虚函数来实现。确定。通过类继承关系和虚函数来实现。在编译的过程中确定了同名操作的具体在编译的过程中确定了同名操作的具体操作对象。通过函数的重载和运算符的操作对象。通过函数的重载和运算符的重载来实现。重载来实现。l 虚函数是动态联
39、编的基础,虚函数经过派生之后,在类族中就虚函数是动态联编的基础,虚函数经过派生之后,在类族中就可以实现运行时多态。可以实现运行时多态。l 根据赋值兼容规则,可使用派生类的对象代替基类对象。如果根据赋值兼容规则,可使用派生类的对象代替基类对象。如果用基类类型的指针指向派生类的对象,就可以通过这个指针来用基类类型的指针指向派生类的对象,就可以通过这个指针来访问该对象,但只能访问从基类继承来的成员。访问该对象,但只能访问从基类继承来的成员。l 如果需要通过基类的指针指向派生类的对象,并访问某个与基如果需要通过基类的指针指向派生类的对象,并访问某个与基类同名的函数成员,可以在基类中将这个同名函数说明为
40、虚函类同名的函数成员,可以在基类中将这个同名函数说明为虚函数。这样,通过基类类型的指针,就可以使属于不同派生类的数。这样,通过基类类型的指针,就可以使属于不同派生类的不同对象产生不同的行为,从而实现运行时多态。不同对象产生不同的行为,从而实现运行时多态。虚函数虚函数为指明某个成员函数具有多态性,要在类的声明中使用关为指明某个成员函数具有多态性,要在类的声明中使用关键字键字virtualvirtual限定该成员函数,将其标志其为虚函数。限定该成员函数,将其标志其为虚函数。 virtual virtual 返回类型返回类型 函数名(参数表)函数名(参数表) ;55虚函数虚函数l关键字关键字virt
41、ualvirtual指明该成员函数为虚函数。指明该成员函数为虚函数。virtualvirtual仅用于类仅用于类的声明内部的成员函数原型声明或定义中,的声明内部的成员函数原型声明或定义中,如果成员函数定义如果成员函数定义在类外,在类外,virtual只能加在函数声明前面,不能加在函数定义前只能加在函数声明前面,不能加在函数定义前面。面。l当一个类的某个成员函数被声明为当一个类的某个成员函数被声明为virtualvirtual,则由该类派生出,则由该类派生出来的所有子类中,该函数始终保持虚函数的特征。来的所有子类中,该函数始终保持虚函数的特征。( (自动向下带自动向下带给其子类给其子类) )。l
42、若未加关键字若未加关键字virtualvirtual,则是派生类中的新成员函数覆盖基类,则是派生类中的新成员函数覆盖基类同名成员函数(参数表必须一样,否则是重载),可称为同名同名成员函数(参数表必须一样,否则是重载),可称为同名覆盖函数,它不能实现运行时的多态性。覆盖函数,它不能实现运行时的多态性。l虚函数实例:虚函数实例:ch16_2.cppch16_2.cpp, ch16_3.cpp ch16_3.cpp5657/ch16_3.cpp/ch16_3.cpp#include#include#include#includeclass Shapeclass Shape public: publi
43、c: Shape(double x, double y) : Shape(double x, double y) : xCoord(x), yCoord(y) xCoord(x), yCoord(y) virtual virtual double Area( ) const double Area( ) const return 0.0; return 0.0; protected:protected: double xCoord, yCoord; double xCoord, yCoord;class Circle :public Shapeclass Circle :public Shap
44、e public: public: Circle(double x, double y, double r) Circle(double x, double y, double r) : Shape(x, y), radius(r) : Shape(x, y), radius(r) virtual virtual double Area( ) const double Area( ) const return 3.14 return 3.14* *radiusradius* *radius; radius; protected: protected: double radius; double
45、 radius;class Rectangle: public Shapeclass Rectangle: public Shape public: public: Rectangle(double x1, double y1, Rectangle(double x1, double y1, double x2, double y2): Shape(x1,y1), double x2, double y2): Shape(x1,y1), x2Coord(x2), y2Coord(y2) x2Coord(x2), y2Coord(y2) virtualvirtual double Area( )
46、 const; double Area( ) const; protected: protected: double x2Coord, y2Coord; double x2Coord, y2Coord;double Rectangle:Area( ) constdouble Rectangle:Area( ) const return return fabs(xCoord-x2Coord) fabs(xCoord-x2Coord)* *(yCoord-y2Coord);(yCoord-y2Coord); void void funfun(const Shape & sp)(const
47、Shape & sp) cout sp.Area() endl; cout sp.Area() endl; void main()void main() Circle c(2.0, 5.0, 4.0) ; Circle c(2.0, 5.0, 4.0) ; fun(c); fun(c); Rectangle t(2.0, 4.0, 1.0, 2.0); Rectangle t(2.0, 4.0, 1.0, 2.0); fun(t); fun(t); 58使用虚函数的注意事项使用虚函数的注意事项l当在派生类中重新定义虚函数(覆盖)时,不必加关键字当在派生类中重新定义虚函数(覆盖)时,不必
48、加关键字virtualvirtual。但重新定义时。但重新定义时不仅不仅要同名,要同名,而且而且它的参数表和返回它的参数表和返回类型全部与基类中的虚函数一样,否则出错。类型全部与基类中的虚函数一样,否则出错。l如果虚函数在基类与子类中出现的仅仅是名字相同,而参如果虚函数在基类与子类中出现的仅仅是名字相同,而参数不同,或返回类型不同,即使写上了关键字数不同,或返回类型不同,即使写上了关键字virtual,也被,也被认为是重载,而不是虚函数,不进行迟后联编。认为是重载,而不是虚函数,不进行迟后联编。例如,下列程序中,在派生类中重载了基类的成员函数,尽例如,下列程序中,在派生类中重载了基类的成员函数
49、,尽管标上了管标上了virtual,但运行中起不到多态效果:,但运行中起不到多态效果: ch16_4.cpp ch16_4.cppl(特例)若基类中的虚函数返回一个基类指针或引用,子(特例)若基类中的虚函数返回一个基类指针或引用,子类中的虚函数返回一个子类的指针或引用,则类中的虚函数返回一个子类的指针或引用,则c+将其视为同将其视为同名虚函数,进行迟后联编。名虚函数,进行迟后联编。例如,例如, ch16_5.cpp59/ ch16_5.cpp/ ch16_5.cpp#include #include class Baseclass Base public: public: virtual Ba
50、se virtual Base * * afn() afn() cout This is Base class.n; cout This is Base class.n; return this; return this; ;class SubClass: public Baseclass SubClass: public Base public: public: SubClass SubClass * * afn() afn() /实际是虚函数实际是虚函数 cout This is SubClass.n; cout This is SubClass.n; return this; return this; ;void test(Base& x)void test(Base& x) Base Base* * b; b; b = x.afn(); b = x.afn(); /迟后联编迟后联编 voi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026届广西北部湾经济区市级名校初三下期末考试(数学试题理)试卷含解析
- 护理查房中的跨学科合作
- 急性呼吸窘迫综合征护理
- 护理学基础:环境因素与病人睡眠质量
- 2026三年级数学下册 小数创新应用
- 2026年医疗废物分类处理院感专项考核试题及答案
- 护理不良事件分级沟通技巧
- 德育工作责任制度
- 快递责任制度
- 护理不良事件数据统计
- 广电网络面试准备及问题预测集
- 2026甘肃省公务员考试题及答案行测
- 2025年青海省公务员考试职业能力测试真题试卷(含答案)
- 2025及未来5年中国棉连衣裙市场调查、数据监测研究报告
- DG-TJ 08-2335-2020 郊野公园设计标准
- 乡镇卫生健康知识培训班课件
- 马克思宗教观课件
- GB/T 17616-2025钢铁及合金牌号统一数字代号体系
- 2025年军考化学试卷真题及答案
- 2024-2025学年度安徽广播影视职业技术学院单招《职业适应性测试》考试历年机考真题集及完整答案详解【历年真题】
- 鲁交安A、B、C证题库
评论
0/150
提交评论