收藏 分享(赏)

《高级语言程序设计》课件第11章继承和多态.pptx

上传人:bubibi 文档编号:21797197 上传时间:2024-05-23 格式:PPTX 页数:30 大小:199.36KB
下载 相关 举报
《高级语言程序设计》课件第11章继承和多态.pptx_第1页
第1页 / 共30页
《高级语言程序设计》课件第11章继承和多态.pptx_第2页
第2页 / 共30页
《高级语言程序设计》课件第11章继承和多态.pptx_第3页
第3页 / 共30页
《高级语言程序设计》课件第11章继承和多态.pptx_第4页
第4页 / 共30页
《高级语言程序设计》课件第11章继承和多态.pptx_第5页
第5页 / 共30页
亲,该文档总共30页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

1、第11章 继承和多态11.1 继承的概念11.2 派生类的定义11.3 派生类成员的访问属性11.4 派生类的构造函数11.5 基类与派生类的转换11.6 多态的概念11.7 函数重载11.8 虚函数11.9 静态关联与动态关联11.10 运算符重载及其重载规则第11章 继承和多态11.1 继承的概念第11章 继承和多态一个新类从已有的类那里获得其特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一角度说,从已有的类(父类)产生一个新的子类,又称为类的派生。继承是一种连接类的层次模型,提供了一种明确表述共性的方法。派生是继承的产物,派生是通过继承基类中原有的

2、特性,并在此基础上修改原有功能或添加一些新的功能来产生新的类。从图11-1中可以看出,小学生、中学生、大学生、研究生是学生的具体化,他们是在学生的共性基础上增加某些特点形成的子类。学生是对各类学生共性的综合,是对各类具体学生特点的抽象。基类综合了派生类的公共特征,派生类则在基类的基础上增加某些特性,把抽象类变成具体的、实用的类型。类的继承是用已有的类来建立新类的编程技术。派生类继承了基类的所有数据成员和成员函数,并可以对成员作必要的增加或调整。一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的。一代一代地派生下去,就形成类的继承层次结构。相

3、当于一个大家族,里面有很多分支,所有的子孙后代都继承了父辈的基本特征,同时又根据需要调整和扩充了原有特征。第11章 继承和多态一个派生类只从一个基类派生,这称为单继承,这种继承关系所形成的层次是一个树形结构,如图11-1所示。此外,一个派生类不仅可以从一个基类派生,还可以从多个基类派生。也就是说,一个派生类有两个或多个基类(一个子类可以有两个或多个父类),这称为多重继承。第11章 继承和多态11.2 派生类的定义第11章 继承和多态声明派生类的一般形式为继承方式包括public(公用的)、private(私有的)和protected(受保护的)。继承方式是可选的,如果不写此项,则默认为priv

4、ate(私有的)。派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分。从基类继承的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性,如图11-2所示。第11章 继承和多态实际上,并不是把基类的成员和派生类自己增加的成员简单地加在一起就成为派生类。构造一个派生类包括三部分工作:(1)从基类接收成员。派生类把基类全部的成员接收过来。这个过程是没有选择的,只能接收基类的全部成员,不能只接收其中一部分成员,舍弃另一部分成员。(2)调整从基类接收的成员。从基类接收成员是没有选择的,但可以对这些成员作某些调整。例如,可以改变基类成员在派生类中的访问属性,这是通过指定继承

5、方式来实现的。可以在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员。但应注意,如果要覆盖基类的成员函数,应使用相同的函数名和参数列表。采用这样的方法可以用新成员取代基类的成员。(3)在声明派生类时增加成员。这部分新增的成员,体现了派生类对基类功能的扩展。注意要根据需要仔细考虑应当增加哪些成员,精心设计。最后,在声明派生类时,一般还应当自己定义派生类的构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的。派生类是基类定义的延续。在实际工作中,可以先声明一个基类,在此基类中只提供某些最基本的功能,而另外一些功能并未实现,然后在声明派生类时加入某些具体的功能,形

6、成适用于某一特定应用的派生类。通过对基类声明的延续,将一个抽象的基类转化成具体的派生类。因此,派生类是抽象基类的具体实现。第11章 继承和多态11.3 派生类成员的访问属性第11章 继承和多态既然派生类中包含基类成员和派生类自己增加的成员,就产生了这两部分成员的关系和访问属性的问题。不同的继承方式决定了基类成员在派生类中的访问属性。(1)公用继承:基类的公用成员和保护成员在派生类中保持原有的访问属性,派生类无法访问基类的私有成员。(2)私有继承:基类的公用成员和保护成员在派生类中成了私有成员,派生类无法访问基类的私有成员。(3)保护继承:基类的公用成员和保护成员在派生类中成了保护成员,派生类无

7、法访问基类的私有成员。保护成员的意思是:不能被外界引用,但可以被派生类的成员引用。11.3.1 公用继承在定义一个派生类时将基类的继承方式指定为public的,称为公用继承;用公用继承方式建立的派生类称为公用派生类,其基类称为公用基类。采用公用继承方式时,基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性,而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以引用它,而不能被派生类的成员函数引用,因此就成为派生类中的不可访问的成员。私有成员体现了数据的封装性,隐藏私有成员有利于大规模程序开发。如果把基类的私有成员的访问权限都原封不

8、动地继承到派生类,使基类的私有成员在派生类中仍保持其私有性质,派生类成员能访问基类的私有成员,这就破坏了数据的封装性。因此,不论是何种继承方式,基类的私有成员对派生类来讲都是不可访问的。第11章 继承和多态11.3.2 私有继承在声明一个派生类时将基类的继承方式指定为private的,称为私有继承;用私有继承方式建立的派生类称为私有派生类,其基类称为私有基类。私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问它们,而在派生类外不能访问它们。私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们。一个基类成员在基类中的访问属

9、性和在派生类中的访问属性可能是不同的。私有基类的成员可以被基类的成员函数访问,但不能被派生类的成员函数访问。图11-3表示了Student各成员在派生类中的访问属性。既然声明为私有继承,就表示将原来能被外界引用的成员隐藏起来,不让外界引用。所以,私有基类的公用成员和保护成员就成为派生类中的私有成员。私有基类的私有成员按规定只能被基类的成员函数引用,它们在派生类中是不可见的和不可访问的。第11章 继承和多态(1)不能通过派生类对象引用从私有基类继承过来的任何成员。(2)派生类的成员函数不能访问私有基类的私有成员,但可以访问私有基类的公用成员。11.3.3 保护继承“受保护”与private和pu

10、blic一样,是用来声明成员的访问权限的。由protected声明的成员称为“受保护的成员”,或简称“保护成员”。保护成员不能被类外访问,这点和私有成员相同。从类的用户角度来看,保护成员等价于私有成员。如果基类声明了私有成员,那么任何派生类都不能访问基类的私有成员;若希望在派生类中能访问它们,应当把这些成员声明为保护成员。如果在一个类中声明了保护成员,就意味着该类可能要用作基类,在它的派生类中会访问这些成员。在定义一个派生类时将基类的继承方式指定为protected的,称为保护继承,如图11-4所示;用保护继承方式建立的派生类称为保护派生类,其基类称为保护基类。保护继承的特点是:(1)保护基类

11、的所有成员在派生类中都被保护起来,类外不能访问,公用成员和保护成员可以被其派生类的成员函数访问。(2)基类的私有成员被派生类继承后变为不可访问的成员,派生类中的一切成员均无法访问它们。如果需要在派生类中引用基类的某些成员,应当将基类的这些成员声明为protected,而不要声明为private。(3)比较一下私有继承和保护继承可以发现,在直接派生类中,以上两种继承方式的作用实际上是相同的:在类外不能访问任何成员,而在派生类中可以通过成员函数访问基类中的公用成员和保护成员。第11章 继承和多态第11章 继承和多态11.4 派生类的构造函数 第11章 继承和多态以前介绍过的类,其数据成员都是标准类

12、型(如int、char)或系统提供的类型(如string),实际上,类的数据成员中还可以包含类对象,如可以在声明一个类时包含如下的数据成员:这时,roommate就是类对象中的内嵌对象,称为子对象(subobject),即对象中的对象。归纳起来,定义派生类构造函数的一般形式为 执行派生类构造函数的顺序是:调用基类构造函数,对基类数据成员初始化;调用子对象构造函数,对子对象的数据成员初始化;对派生类数据成员初始化。第11章 继承和多态11.5 基类与派生类的转换第11章 继承和多态公用继承是最常见的,也只有公用继承能较好地保留基类的特征。公用继承保留了除构造函数和析构函数外的基类所有成员,基类的

13、公用或保护成员的访问权限在派生类中都按原样保留了下来,在派生类外可以调用基类的公用成员函数以访问基类的私有成员。因此,公用派生类具有基类的全部功能,所有基类能够实现的功能,公用派生类都能实现。而私有派生类或保护派生类不能实现基类的全部功能。因此,从某种意义上讲,只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。不同类型数据之间在一定条件下可以进行类型的转换。例如,整型数据可以赋值给双精度型变量,在赋值之前,先把整型数据转换成双精度型数据。这种不同类型数据之间的自动转换和赋值,称为赋值兼容。基类与派生类对象之间有赋值兼容关系,可以进行类型间的转换。由于派生类中包含从基类继承的成员,因

14、此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其公用派生类对象代替。这具体表现在以下几个方面:(1)派生类对象可以向基类对象赋值。可以用子类(即公用派生类)对象对其基类对象赋值。(2)派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化。(3)如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。(4)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。第11章 继承和多态11.6 多态的概念第11章 继承和多态多态性(polymorphism)是面向对象程序设计的一个重要特征。顾名思义,多态的意思是一个事物有多

15、种形态。例如,日常生活中最常见的水就有多种形态:固态、液态、气态。在面向对象方法中,多态是相同的消息在被不同的对象接收时产生的不同行为。也就是说,每个对象可以用自己的方式去响应相同的消息。所谓消息,可以理解为函数调用,当对象处理消息时,根据参数的不同,调用同名函数,产生不同的行为,这也就是我们所说的多态。多态可以分为两类:静态多态性和动态多态性。静态多态性是通过函数的重载实现的。在程序编译时,系统就能决定调用哪个函数,因此静态多态性又称编译时的多态性。静态多态性的函数调用速度快、效率高,但缺乏灵活性,在程序运行前就应决定执行的函数。动态多态性又称为运行时多态性。动态多态性不在编译时确定调用的是

16、哪个函数,而在程序运行过程中才动态地确定操作所针对的对象。动态多态性是通过虚函数来实现的。在面向对象程序设计中,封装用以隐藏细节,实现代码模块化;继承用以扩展已存在的功能,实现代码重用;多态则是为了实现接口重用。不论对象千变万化,用户都是用同一形式的消息去调用它们,使它们根据编写好的函数分别作出反应。第11章 继承和多态11.7 函 数 重 载第11章 继承和多态C+支持两种重载:函数重载和运算符重载。函数重载是指完成不同功能的多个函数具有相同的名字、不同的形参列表。所谓形参列表不同,指的是参数的个数不同,或参数个数相同但其类型不同。重载函数在调用时,编译系统会将实参的类型和所有重名的重载函数

17、的形参列表逐一比较,根据下面的先后顺序来寻找并调用实际的被调函数。(1)寻找一个严格的匹配。(2)通过参数类型的兼容转换寻找一个匹配。第11章 继承和多态11.8 虚 函 数第11章 继承和多态在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。人们提出这样的设想:能否用同一个调用形式,既能调用基类又能调用派生类的同名函数。在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们。C+中的虚函数就是用来实现这个设想的。所谓虚函数,就是在基类中声明函数是虚拟的,并不是实际存在的函数,然后在派生类中才正式定义此函数。在程序运行期间,用

18、指针来指向某一派生类对象,这样就能调用指针所指向的派生类对象中的函数,而不会调用其他派生类中的函数。虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。本来,基类指针是用来指向基类对象的,如果用它指向派生类对象,则自动进行指针类型转换,将派生类的对象的指针先转换为基类的指针。这样,基类指针指向的是派生类对象中的基类部分。在程序修改之前,是无法通过基类指针去调用派生类对象中的成员函数的。虚函数则突破了这一限制。基类中的display被声明为虚函数,在声明派生类时被重载,这时派生类的同名函数就取代了其基类中的虚函数。因此,在使基类指针指向

19、派生类对象后,调用display函数就调用了派生类的display函数。要注意的是,只有用virtual声明函数为虚函数后才具有以上作用;如果不声明虚函数,则通过基类指针调用派生类的非虚函数的做法是行不通的。第11章 继承和多态11.9 静态关联与动态关联第11章 继承和多态先定义了一个指向基类的指针变量,并使它指向相应的类对象,然后通过这个基类指针去调用虚函数。显然,对这样的调用方式,编译系统在编译该行代码时是无法确定调用哪一个类对象的虚函数的。在这样的情况下,编译系统把它放到运行阶段处理,在运行阶段确定关联关系。在运行阶段,基类指针变量先指向了某一个类对象,然后通过此指针变量调用该对象中的

20、函数。由虚函数实现的动态多态性就是:同一类族中不同类的对象,对同一函数调用可以作出不同的响应。虚函数的使用方法如下:(1)在基类用virtual声明成员函数为虚函数,这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地调用。在类外定义虚函数时,不必再加virtual。(2)在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。(3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。(4)通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。(5)一个成员函数被声明为虚函数

21、后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同函数名、参数列表、返回值的函数。第11章 继承和多态通过虚函数与指向基类对象的指针变量的配合使用,就能实现动态多态性。如果想调用同一类族中不同类的同名函数,只要先用基类指针指向该类对象即可。如果指针不断地指向同一类族中不同类的对象,就能不断地调用这些对象中的同名函数。需要注意的是,有时在基类中定义的非虚函数会在派生类中被重新定义(如例11.11中的display函数),如果用基类指针调用该成员函数,则系统会调用对象中基类部分的成员函数;如果用派生类指针调用该成员函数,则系统会调用派生类对象中的成员函数。这并不是多态性行为

22、(使用的是不同类型的指针),没有用到虚函数的功能。使用虚函数,系统会有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table,简称vtable)。vtable是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的。总的来讲,多态性是非常高效的。第11章 继承和多态11.10 运算符重载及其重载规则第11章 继承和多态1运算符重载所谓重载,就是重新赋予新的含义函数重载就是对一个已有的函数赋予新的含义,使之实现新功能基本数据类型是C+中预定义的数据类型,包括char、int、float及double等,大多数基

23、本数据类型都预定义了相关的运算符,实现相关数据的操作和运算。其实计算机处理整数、单精度数和双精度数加法的操作是很不相同的,但由于C+已经对运算符“+”进行了重载,所以“+”就能适用于int、float、double类型的运算。在面向对象程序设计中,增加了许多用户自定义数据类型,在这些数据类型上也经常需要作相关运算。运算符重载使用户能根据自己的需要对C+已提供的运算符进行重载,赋予它们新的含义。运算符重载可以实现两个对象或对象与其他数据类型的复杂运算。运算符重载的基本原则是:对于给定的C+运算符,通过运算符重载,完成一个特定的操作。通过特定的函数重新定义运算符的功能,使它能够完成作用于类的对象的

24、相关运算。运算符重载的本质是函数的重载。运算符重载的方法是定义一个重载运算符的函数,使得指定的运算符不仅能实现原有的功能,而且能实现在函数中指定的新的功能。在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算也就是说,运算符重载是通过定义函数实现的运算符重载实质上是函数的重载 重载运算符的函数的一般格式为第11章 继承和多态函数名是由operator和运算符共同组成的。上面的“operator+”就是函数名,意思是“对运算符+重载的函数”。只要掌握了这一点,就会发现,这类函数和其他函数在形式上并没有什么区别。在实际工作中,类的声明和类的使用往往是分离的假如在声明Complex类时

25、,对运算符+、-、*、/都进行了重载,那么使用这个类的用户在编程时可以完全不考虑函数是怎么实现的,放心大胆地直接使用+、-、*、/进行复数的运算,十分方便。需要说明的是:运算符被重载后,其原有的功能仍然保留,没有丧失或改变。例如,运算符“+”仍然可以用于int、float、double及char等类型数据的运算,又增加了用于复数的功能。那么,编译系统是如何判断该执行哪一个功能的呢?这是由表达式的上下文决定的,即根据运算符两侧(如果是单目运算符则为一侧)的数据类型来决定的。C+提供的运算符只能用于C+的标准类型数据的运算,但C+程序设计的重要基础是类和对象,如果C+的运算符都无法用于类对象,则类

26、和对象的应用将会受到很大的限制,影响类和对象的使用。为了解决这个问题,使类和对象有更大的生命力,C+采取的方法不是为类对象另外定义一批新的运算符,而是允许重载现有的运算符,使这些简单易用、众所周知的运算符能够直接应用于自己定义的类对象。通过运算符重载,C+中扩大了已有运算符的作用范围,使之能用于类对象。运算符重载对C+有重要的意义,把运算符重载和类结合起来,可以在C+程序中定义出很有实用意义而又使用方便的新的数据类型运算符重载使C+具有更强大的功能更好的可扩充性和适应性。第11章 继承和多态2单目运算符重载单目运算符只有一个操作数,如!a、-b、&c、*p,还有最常用的+i和-i等。单目运算符

27、重载的方法与双目运算符重载的方法类似。但由于单目运算符只有一个操作数,因此运算符重载函数只有一个参数;如果运算符重载函数作为成员函数,则还可省略此参数。3运算符重载的规则(1)C+不允许用户自己定义新的运算符,只能对已有的C+运算符进行重载。C+中绝大部分的运算符允许重载,具体规定见表11.1。第11章 继承和多态(2)重载不能改变运算符运算对象(即操作数)的个数。(3)重载不能改变运算符的优先级别。(4)重载不能改变运算符的结合性。(5)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。(6)应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高中资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:文库网官方知乎号:文库网

经营许可证编号: 粤ICP备2021046453号世界地图

文库网官网©版权所有2025营业执照举报