1、1C+程序设计第第5 5章章 多态性与虚函数多态性与虚函数2第第5章章 多态性与虚函数多态性与虚函数 什么是多态性什么是多态性 向上类型转换向上类型转换 功能早绑定和晚绑定功能早绑定和晚绑定 实现功能晚绑定实现功能晚绑定虚函数虚函数 纯虚函数和抽象类纯虚函数和抽象类 学生信息管理系统中的多态性学生信息管理系统中的多态性本章学习要点本章学习要点3第第5章章 多态性与虚函数多态性与虚函数在面向对象方法中在面向对象方法中一般是这样表述一般是这样表述多态性多态性的:的:向不同的对象发送同一个消息,不同的对象在接向不同的对象发送同一个消息,不同的对象在接收时会有不同的反应,产生不同的动作。收时会有不同的
2、反应,产生不同的动作。也就是也就是说,每个对象可以用自己的方式去响应共同的消说,每个对象可以用自己的方式去响应共同的消息。息。在在C+程序设计中程序设计中,多态性多态性是指用一个名字定是指用一个名字定义不同的函数,这些函数执行不同但又类似的操义不同的函数,这些函数执行不同但又类似的操作,从而可以使用相同的调用方式来调用这些具作,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数。有不同功能的同名函数。实现实现“一个接口,多种方法一个接口,多种方法”45.1 什么是多态性什么是多态性C+中的多态性可以分为中的多态性可以分为4类:类:参数多态参数多态包含多态包含多态重载多态重载多态强制多态
3、强制多态55.1 什么是多态性什么是多态性 参数多态参数多态 如函数模板和类模板。如函数模板和类模板。l由函数模板实例化的各个函数都具有相由函数模板实例化的各个函数都具有相同的操作,而这些函数的参数类型却各不同的操作,而这些函数的参数类型却各不相同。同样地,由类模板实例化的各个类相同。同样地,由类模板实例化的各个类都具有相同的操作,而操作对象的类型是都具有相同的操作,而操作对象的类型是各不相同的。各不相同的。65.1 什么是多态性什么是多态性包含多态包含多态l是研究类族中定义于不同类中的同名成是研究类族中定义于不同类中的同名成员函数的多态行为,主要是通过虚函数来员函数的多态行为,主要是通过虚函
4、数来实现的。实现的。75.1 什么是多态性什么是多态性重载多态重载多态l如函数重载、运算符重载等。前面我们如函数重载、运算符重载等。前面我们学习过的普通函数及类的成员函数的重载学习过的普通函数及类的成员函数的重载都属于重载多态。都属于重载多态。85.1 什么是多态性什么是多态性强制多态强制多态l是指将一个变元的类型加以变化,以符是指将一个变元的类型加以变化,以符合一个函数(或操作)的要求,例如加法合一个函数(或操作)的要求,例如加法运算符在进行浮点数与整型数相加时,首运算符在进行浮点数与整型数相加时,首先进行类型强制转换,把整型数变为浮点先进行类型强制转换,把整型数变为浮点数再相加的情况,就是
5、强制多态的实例。数再相加的情况,就是强制多态的实例。95.2 向上类型转换向上类型转换向上类型转换向上类型转换是指把一个派生类的对象作是指把一个派生类的对象作为基类的对象来使用为基类的对象来使用。向上转型中有三点需要我们特别向上转型中有三点需要我们特别注意:注意:向上类型转换是安全的。向上类型转换是安全的。向上类型转换可以自动完成。向上类型转换可以自动完成。向上类型转换的过程中会丢失子类型信向上类型转换的过程中会丢失子类型信息。息。105.2 向上类型转换向上类型转换【例例5-1】一个向上类型转换的例子。一个向上类型转换的例子。#include using namespace std;clas
6、s B0 /基类基类B0声明声明public:void display()/公有成员函数公有成员函数 coutB0:display()endl;class B1:public B0public:void display()coutB1:display()endl;class D1:public B1public:void display()coutD1:display()display();int main()B0 b0;/声明声明B0类对象类对象 B1 b1;/声明声明B1类对象类对象 D1 d1;/声明声明D1类对象类对象 B0*p;/声明声明B0类指针类指针p p=&b0;/B0类指针类
7、指针p指向指向B0类对象类对象 fun(p);p=&b1;/B0类指针类指针p指向指向B1类对象类对象 fun(p);p=&d1;/B0类指针类指针p指向指向D1类对象类对象 fun(p);return 0;程序运行结果如下:程序运行结果如下:B0:display()B1:display()B2:display()virtual115.2 向上类型转换向上类型转换【例例5-1】一个向上类型转换的例子。一个向上类型转换的例子。#include using namespace std;class B0 /基类基类B0声明声明public:void display()/公有成员函数公有成员函数 co
8、utB0:display()endl;class B1:public B0public:void display()coutB1:display()endl;class D1:public B1public:void display()coutD1:display()display();int main()B0 b0;/声明声明B0类对象类对象 B1 b1;/声明声明B1类对象类对象 D1 d1;/声明声明D1类对象类对象 B0*p;/声明声明B0类指针类指针p p=&b0;/B0类指针类指针p指向指向B0类对象类对象 fun(p);p=&b1;/B0类指针类指针p指向指向B1类对象类对象 f
9、un(p);p=&d1;/B0类指针类指针p指向指向D1类对象类对象 fun(p);return 0;程序运行结果如下:程序运行结果如下:B0:display()B0:display()B0:display()125.3 功能早绑定和晚绑定功能早绑定和晚绑定v多态性从实现的角度来讲可以划分为两多态性从实现的角度来讲可以划分为两类:类:编译时的多态性编译时的多态性和和运行时的多态性运行时的多态性。v在在C+C+中,中,编译时的多态性编译时的多态性主要是主要是通过函通过函数重载和运算符重载实现数重载和运算符重载实现的。的。运行时的运行时的多态性多态性主要是主要是通过虚函数来实现通过虚函数来实现的。
10、的。135.3 功能早绑定和晚绑定功能早绑定和晚绑定v多态性从实现的角度来讲可以划分为两多态性从实现的角度来讲可以划分为两类:编译时的多态性和运行时的多态性。类:编译时的多态性和运行时的多态性。v在在C+C+中,多态性的实现和绑定这一概念中,多态性的实现和绑定这一概念有关。有关。v所谓所谓绑定绑定就是把函数体与函数调用相联就是把函数体与函数调用相联系。系。145.3 功能早绑定和晚绑定功能早绑定和晚绑定v功能早绑定功能早绑定(编译时的多态性编译时的多态性):):绑定是在程序绑定是在程序编译阶段完成。编译阶段完成。功能早绑定时,系统用实参与功能早绑定时,系统用实参与形参进行匹配,对于同名的重载函
11、数便根据参形参进行匹配,对于同名的重载函数便根据参数上的差异进行区分,然后进行绑定,从而实数上的差异进行区分,然后进行绑定,从而实现了编译时的多态性。现了编译时的多态性。v功能晚绑定功能晚绑定(运行时的多态性运行时的多态性):):绑定是在程序绑定是在程序运行阶段完成的。即当程序调用到某一函数名运行阶段完成的。即当程序调用到某一函数名时,才去寻找和连接其程序代码,对面向对象时,才去寻找和连接其程序代码,对面向对象程序设计而言,就是当对象接收到某一消息时,程序设计而言,就是当对象接收到某一消息时,才去寻找和连接相应的方法。才去寻找和连接相应的方法。155.3 功能早绑定和晚绑定功能早绑定和晚绑定v
12、一般而言,编译型语言一般而言,编译型语言(如如C C、PASCAL)PASCAL)都采用都采用功能早绑定,而解释性语言功能早绑定,而解释性语言(如如LISPLISP、Prolog)Prolog)都采用功能晚绑定。都采用功能晚绑定。v功能早绑定要求在程序编译时就知道调用函数功能早绑定要求在程序编译时就知道调用函数的全部信息,因此,这种绑定类型的函数调用的全部信息,因此,这种绑定类型的函数调用速度很快,效率高,但缺乏灵活性;速度很快,效率高,但缺乏灵活性;v而功能晚绑定方式恰好相反,采用这种绑定方而功能晚绑定方式恰好相反,采用这种绑定方式,一直要到程序运行时才能确定调用哪个函式,一直要到程序运行时
13、才能确定调用哪个函数,它降低了程序的运行效率,但增强了程序数,它降低了程序的运行效率,但增强了程序的灵活性。的灵活性。165.3 功能早绑定和晚绑定功能早绑定和晚绑定vC+C+由由C C发展而来,为了保持发展而来,为了保持C C语言的高效性,语言的高效性,C+C+仍是编译型的,仍采用功能早绑定。好在仍是编译型的,仍采用功能早绑定。好在C+C+的设计者想出了的设计者想出了“虚函数虚函数”的机制的机制,利用虚利用虚函数机制,函数机制,C+C+可部分地采用功能早绑定。这可部分地采用功能早绑定。这就是说,就是说,C+C+实际上是采用了功能早绑定和功实际上是采用了功能早绑定和功能晚绑定相结合的编译方法。
14、能晚绑定相结合的编译方法。175.4.1 虚函数的定义与使用虚函数的定义与使用v虚成员函数的定义语法是:虚成员函数的定义语法是:virtualvirtual 函数类型函数类型 函数名函数名(形参表形参表)函数体函数体 虚函数的定义是在基类中进行的虚函数的定义是在基类中进行的,在成员函数原型的声,在成员函数原型的声明语句之前冠以关键字明语句之前冠以关键字virtual,从而提供一种接口。,从而提供一种接口。18v当基类中的某个成员函数被声明为虚函数后,当基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重此虚函数就可以在一个或多个派生类中被重新定义。新定义。在派生类中重新
15、定义时在派生类中重新定义时,其函数原其函数原型,包括返回类型、函数名、参数个数、参型,包括返回类型、函数名、参数个数、参数类型的顺序,都必须与基类中的原型完全数类型的顺序,都必须与基类中的原型完全相同。相同。5.4.1 虚函数的定义与使用虚函数的定义与使用19v虚函数的作用虚函数的作用是允许在派生类中重新定义与是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数引用来访问基类和派生类中的同名函数。v具体做法是,首先在基类中声明这个成员函具体做法是,首先在基类中声明这个成员函数为虚函数,也就是在这个成员函数前面缀
16、数为虚函数,也就是在这个成员函数前面缀上关键字上关键字virtualvirtual,并在派生类中被重新定义,并在派生类中被重新定义,就能实现动态调用的功能。就能实现动态调用的功能。5.4.1 虚函数的定义与使用虚函数的定义与使用205.2 向上类型转换向上类型转换【例例5-35-3】有一个交通工具类有一个交通工具类vehiclevehicle,将它作为基类派,将它作为基类派生出汽车类生出汽车类motor_ vehicle motor_ vehicle,再将汽车类,再将汽车类motor_ motor_ vehicle vehicle 作为基类派生出小汽车类作为基类派生出小汽车类carcar和卡车
17、类和卡车类trucktruck,声明这些类并定义一个虚函数用来显示各类信息。程序声明这些类并定义一个虚函数用来显示各类信息。程序如下:如下:#include#include using namespace std;using namespace std;class class vehiclevehicle/基类基类vehiclevehicle声明声明public:public:virtual virtual void message()/void message()/虚成员函数虚成员函数 coutvehicle message endl;coutvehicle message endl;pri
18、vate:private:int wheels;/int wheels;/车轮个数车轮个数 float weight;/float weight;/车重车重;215.2 向上类型转换向上类型转换class class motor_vehiclemotor_vehicle:public:public vehicle vehicle public:public:void message()void message()cout motor_ vehicle message endl;cout motor_ vehicle message endl;private:int passengers;pri
19、vate:int passengers;/承载人数承载人数;class class carcar:public:public motor_vehiclemotor_vehicle public:public:void message()coutcar message endl;void message()coutcar message endl;private:float engine;private:float engine;/发动机的马力数发动机的马力数;class class trucktruck:public:public motor_vehiclemotor_vehicle publ
20、ic:public:void message()cout truck message endl;void message()cout truck message message();p-message();p=&m;p=&m;p-message();p-message();p=&c;p=&c;p-message();p-message();p=&t;p=&t;p-message();p-message();return 0;return 0;23vC+C+规定,如果在派生类中,没有用规定,如果在派生类中,没有用virtualvirtual显式地给出虚函数声明,这时系统就会遵循以显式地给出虚函数
21、声明,这时系统就会遵循以下的规则来判断一个成员函数是不是虚函数:下的规则来判断一个成员函数是不是虚函数:(1 1)该函数与基类的虚函数)该函数与基类的虚函数有相同的名称有相同的名称。(2 2)该函数与基类的虚函数)该函数与基类的虚函数有相同的参数有相同的参数个数及相同的对应参数类型个数及相同的对应参数类型。(3 3)该函数与基类的虚函数)该函数与基类的虚函数有相同的返回有相同的返回类型或者满足赋值兼容规则的指针、引用型类型或者满足赋值兼容规则的指针、引用型的返回类型的返回类型。5.4.1 虚函数的定义与使用虚函数的定义与使用24说明:说明:v(1)(1)通过定义虚函数来使用通过定义虚函数来使用
22、C+C+提供的多态性提供的多态性机制时,派生类应该从它的基类公用派生。机制时,派生类应该从它的基类公用派生。之所以有这个要求,是因为我们是在赋值兼之所以有这个要求,是因为我们是在赋值兼容规则的基础上来使用虚函数的,而赋值兼容规则的基础上来使用虚函数的,而赋值兼容规则成立的前提条件是派生类从其基类公容规则成立的前提条件是派生类从其基类公用派生。用派生。5.4.1 虚函数的定义与使用虚函数的定义与使用25说明:说明:v(2)(2)必须首先在基类中定义虚函数。由于必须首先在基类中定义虚函数。由于“基类基类”与与“派生类派生类”是相对的,因此,是相对的,因此,这项说明并不表明必须在类等级的最高层这项说
23、明并不表明必须在类等级的最高层类中声明虚函数。在实际应用中,应该在类中声明虚函数。在实际应用中,应该在类等级内需要具有动态多态性的几个层次类等级内需要具有动态多态性的几个层次中的最高层类内首先声明虚函数。中的最高层类内首先声明虚函数。5.4.1 虚函数的定义与使用虚函数的定义与使用26说明:说明:v(3)(3)在派生类对基类中声明的虚函数进行重新在派生类对基类中声明的虚函数进行重新定义时,关键字定义时,关键字virtualvirtual可以写也可以不写。可以写也可以不写。但为了增强程序的可读性,最好在对派生类但为了增强程序的可读性,最好在对派生类的虚函数进行重新定义时也加上关键字的虚函数进行重
24、新定义时也加上关键字virtualvirtual。v如果在派生类中没有对基类的虚函数重新定如果在派生类中没有对基类的虚函数重新定义,则派生类简单地继承其直接基类的虚函义,则派生类简单地继承其直接基类的虚函数。数。5.4.1 虚函数的定义与使用虚函数的定义与使用27说明:说明:v(4)(4)虽然使用对象名和点运算符的方式也可以虽然使用对象名和点运算符的方式也可以调用虚函数,例如语句:调用虚函数,例如语句:c.message();c.message();可以调用虚函数可以调用虚函数car:message()car:message()。但是这。但是这种调用是在编译时进行的功能早绑定,它没有种调用是在
25、编译时进行的功能早绑定,它没有充分利用虚函数的特性。只有通过基类指针或充分利用虚函数的特性。只有通过基类指针或引用访问虚函数时才能获得运行时的多态性。引用访问虚函数时才能获得运行时的多态性。5.4.1 虚函数的定义与使用虚函数的定义与使用28说明:说明:v(5)(5)一个虚函数无论被公用继承多少次,它仍一个虚函数无论被公用继承多少次,它仍然保持其虚函数的特性。然保持其虚函数的特性。v(6)(6)虚函数必须是其所在类的成员函数,而不虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数,因为能是友元函数,也不能是静态成员函数,因为虚函数调用要靠特定的对象来决定该激活哪个虚函数调用
26、要靠特定的对象来决定该激活哪个函数。但是虚函数可以在另一个类中被声明为函数。但是虚函数可以在另一个类中被声明为友元函数。友元函数。5.4.1 虚函数的定义与使用虚函数的定义与使用29说明:说明:v(7 7)内联函数不能是虚函数,因为内联函数)内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的。即使虚函是不能在运行中动态确定其位置的。即使虚函数在类的内部定义,编译时仍将其看作是非内数在类的内部定义,编译时仍将其看作是非内联的。联的。5.4.1 虚函数的定义与使用虚函数的定义与使用30说明:说明:v(8 8)构造函数不能是虚函数。因为虚函数作)构造函数不能是虚函数。因为虚函数作为运行
27、过程中多态的基础,主要是针对对象的,为运行过程中多态的基础,主要是针对对象的,而构造函数是在对象产生之前运行的,因此虚而构造函数是在对象产生之前运行的,因此虚构造函数是没有意义的。构造函数是没有意义的。v(9 9)析构函数可以是虚函数,而且通常说明)析构函数可以是虚函数,而且通常说明为虚函数。为虚函数。5.4.1 虚函数的定义与使用虚函数的定义与使用31v在析构函数前面加上关键字在析构函数前面加上关键字virtualvirtual进进行说明,则称该析构函数为虚析构函行说明,则称该析构函数为虚析构函数。虚析构函数的声明语法为:数。虚析构函数的声明语法为:virtual virtual 类名类名(
28、);();5.4.2 虚析构函数虚析构函数325.2 向上类型转换向上类型转换【例例5-45-4】在交通工具类在交通工具类vehiclevehicle中使用虚析构函数。中使用虚析构函数。#include#include using namespace std;using namespace std;class class vehiclevehicle /基类基类vehiclevehicle声明声明public:public:vehicle()vehicle()/构造函数构造函数 virtual virtual vehiclevehicle()/()/虚析构函数虚析构函数 coutvehicle
29、:coutvehicle:vehicle()endl;vehicle()endl;private:private:int wheels;int wheels;float weight;float weight;335.2 向上类型转换向上类型转换/声明声明vehiclevehicle的公用派生类的公用派生类motor_ vehiclemotor_ vehicleclass class motor_vehiclemotor_vehicle:public:public vehiclevehiclepublic:public:motor_vehicle()/motor_vehicle()/派生类构造
30、函数派生类构造函数 motor_vehiclemotor_vehicle()/()/派生类析构函数派生类析构函数 coutmotor_ vehicle:coutmotor_ vehicle:motor_ vehicle()endl;motor_ vehicle()endl;private:private:int passengers;int passengers;int main()int main()vehicle vehicle*p;p;/声明声明vehiclevehicle类指针类指针p p p=new motor_vehicle;p=new motor_vehicle;delete p
31、;delete p;return 0;return 0;程序运行结果如下:程序运行结果如下:motor_ vehicle:motor_ vehicle()vehicle:vehicle()程序运行结果如下:程序运行结果如下:vehicle:vehicle()345.4.35.4.3虚函数与重载函数的比较虚函数与重载函数的比较 在一个派生类中重新定义基类的虚函数不同于一般的函在一个派生类中重新定义基类的虚函数不同于一般的函数重载:数重载:v函数重载处理的是同一层次上的同名函数问题,而虚函数重载处理的是同一层次上的同名函数问题,而虚函数处理的是同一类族中不同派生层次上的同名函数函数处理的是同一类族
32、中不同派生层次上的同名函数问题,前者是横向重载,后者可以理解为纵向重载。问题,前者是横向重载,后者可以理解为纵向重载。但与重载不同的是但与重载不同的是:同一类族的虚函数的首部是相同同一类族的虚函数的首部是相同的,而函数重载时函数的首部是不同的的,而函数重载时函数的首部是不同的(参数个数或参数个数或类型不同类型不同)。v重载函数可以是成员函数或普通函数,而虚函数只能重载函数可以是成员函数或普通函数,而虚函数只能是成员函数;是成员函数;v重载函数的调用是以所传递参数序列的差别作为调用重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不不同函数的依据;虚函数是
33、根据对象的不同去调用不同类的虚函数;同类的虚函数;v虚函数在运行时表现出多态功能,这是虚函数在运行时表现出多态功能,这是C+C+的精髓;的精髓;而重载函数则在编译时表现出多态性。而重载函数则在编译时表现出多态性。355.5 纯虚函数和抽象类纯虚函数和抽象类v纯虚函数纯虚函数是一个在基类中说明的虚函数,是一个在基类中说明的虚函数,它在该基类中没有定义,但要求在它的派它在该基类中没有定义,但要求在它的派生类中必须定义自己的版本生类中必须定义自己的版本。v纯虚函数的定义形式如下:纯虚函数的定义形式如下:class class 类名类名 virtualvirtual 函数类型函数类型 函数名函数名(参
34、数表参数表)=0=0;365.2 向上类型转换向上类型转换【例例5-55-5】定义一个公共基类定义一个公共基类ShapeShape,它表示一个封闭平面,它表示一个封闭平面几何图形。然后,从几何图形。然后,从ShapeShape类派生出三角形类类派生出三角形类TriangletTrianglet、矩形类矩形类RectangleRectangle和圆类和圆类CircleCircle,在基类中定义纯虚函,在基类中定义纯虚函数数show()show()和和area()area(),分别用于显示图形信息和求相应图,分别用于显示图形信息和求相应图形的面积,并在派生类中根据不同的图形实现相应的函形的面积,并
35、在派生类中根据不同的图形实现相应的函数。要求实现运行时的多态性。数。要求实现运行时的多态性。#include#include#include#includeusing namespace std;using namespace std;const double PI=3.1415926535;const double PI=3.1415926535;class Shape/class Shape/形状类形状类 public:public:virtual void virtual void showshow()=0;()=0;virtual double virtual double areaa
36、rea()=0;()=0;375.2 向上类型转换向上类型转换class Rectangle:public Shape/class Rectangle:public Shape/矩形类矩形类public:public:Rectangle()length=0;width=0;Rectangle()length=0;width=0;Rectangle(double len,double wid)Rectangle(double len,double wid)length=len;width=wid;length=len;width=wid;double double areaarea()retur
37、n length()return length*width;/width;/求面积求面积 void void showshow()()coutlength=lengthtwidth=widthendcoutlength=lengthtwidth=widthendl;l;private:private:double length,width;/double length,width;/长宽长宽;385.2 向上类型转换向上类型转换class Triangle:public Shape/class Triangle:public Shape/三角形类三角形类public:public:Triang
38、le()a=0;b=0;c=0;Triangle()a=0;b=0;c=0;Triangle(double x,double y,double z)Triangle(double x,double y,double z)a=x;b=y;c=z;a=x;b=y;c=z;double double areaarea()/()/求面积求面积 double s=(a+b+c)/2.0;double s=(a+b+c)/2.0;return sqrt(s return sqrt(s*(s-a)(s-a)*(s-b)(s-b)*(s-c);(s-c);void void showshow()()couta
39、=atb=btc=cendl;couta=atb=btc=cendl;private:private:double a,b,c;/double a,b,c;/三角形三边长三角形三边长;395.2 向上类型转换向上类型转换class Circle:public Shape/class Circle:public Shape/圆类圆类public:public:Circle()radius=0;Circle()radius=0;Circle(double r)radius=r;Circle(double r)radius=r;double double areaarea()return PI()r
40、eturn PI*radiusradius*radius;radius;void void showshow()coutradius=radiusendl;()coutradius=radiusendl;private:private:double radius;double radius;405.2 向上类型转换向上类型转换int main()int main()Shape Shape*s;s;Circle c(10);Circle c(10);Rectangle r(6,8);Rectangle r(6,8);Triangle t(3,4,5);Triangle t(3,4,5);c.c.
41、showshow();/();/静态多态静态多态 coutcout圆面积:圆面积:c.area()endl;c.area()s-showshow();();cout cout矩形面积:矩形面积:area()endl;area()s-showshow();();cout cout三角形面积:三角形面积:area()endl;area()endl;return 0;return 0;415.5 纯虚函数和抽象类纯虚函数和抽象类v如果一个类至少有一个纯虚函数,那么就称该类为抽如果一个类至少有一个纯虚函数,那么就称该类为抽象类。象类。v对于抽象类的使用有以下几点规定:对于抽象类的使用有以下几点规定:(
42、1 1)由于抽象类中至少包含一个没有定义功能的)由于抽象类中至少包含一个没有定义功能的纯虚函数。因此,抽象类只能作为其他类的基类来纯虚函数。因此,抽象类只能作为其他类的基类来使用,不能建立抽象类对象,这只能用来为派生类使用,不能建立抽象类对象,这只能用来为派生类提供一个接口规范,其纯虚函数的实现由派生类给提供一个接口规范,其纯虚函数的实现由派生类给出。出。425.5 纯虚函数和抽象类纯虚函数和抽象类v对于抽象类的使用有以下几点规定:对于抽象类的使用有以下几点规定:(2 2)不允许从具体类派生出抽象类,。所谓具体)不允许从具体类派生出抽象类,。所谓具体类,就是不包含纯虚函数的普通类。类,就是不包
43、含纯虚函数的普通类。(3 3)抽象类不能用作参数类型、函数返回类型或)抽象类不能用作参数类型、函数返回类型或显式转换的类型。显式转换的类型。(4 4)可以声明指向抽象类的指针或引用,此指针)可以声明指向抽象类的指针或引用,此指针可以指向它的派生类,进而实现动态多态性。可以指向它的派生类,进而实现动态多态性。435.5 纯虚函数和抽象类纯虚函数和抽象类v对于抽象类的使用有以下几点规定:对于抽象类的使用有以下几点规定:(5 5)如果派生类中没有重新定义纯虚函数,则派)如果派生类中没有重新定义纯虚函数,则派生类只是简单继承基类的纯虚函数,则这个派生类生类只是简单继承基类的纯虚函数,则这个派生类仍然是
44、一个抽象类。如果派生类中给出了基类所有仍然是一个抽象类。如果派生类中给出了基类所有纯虚函数的实现,则该派生类就不再是抽象类了,纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以创建对象的具体类。它是一个可以创建对象的具体类。(6 6)在抽象类中也可以定义普通成员函数或虚函)在抽象类中也可以定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但仍然可以通过数,虽然不能为抽象类声明对象,但仍然可以通过派生类对象来调用这些不是纯虚函数的函数。派生类对象来调用这些不是纯虚函数的函数。44小结与复习建议小结与复习建议v主要内容主要内容 多态性的概念、虚函数、纯虚函数、多态性的概念、虚函数、纯虚函数
45、、抽象类抽象类v达到的目标达到的目标 理解多态的概念,学会运用多态机制。理解多态的概念,学会运用多态机制。451.1.下列下列shapeshape类是一个表示形状的抽象类,类是一个表示形状的抽象类,area()area()为为求图形面积的函数。请从求图形面积的函数。请从shapeshape类派生三角形类类派生三角形类(triangle)(triangle)、矩形类(、矩形类(rectanglerectangle),并用一个函数),并用一个函数printArea()printArea()分别输出以上三者的面积。分别输出以上三者的面积。class shape class shape public:
46、public:virtual double area()const=0;virtual double area()const=0;462.2.下列下列shapeshape类是一个表示形状的抽象类,类是一个表示形状的抽象类,area()area()为为求图形面积的函数,要求:求图形面积的函数,要求:(1)(1)请从请从shapeshape类派生三角形类类派生三角形类(triangle)(triangle)、矩形类、矩形类(rectanglerectangle),并给出具体的求面积函数。),并给出具体的求面积函数。(2)(2)写一个通用的用以求不同形状的图形面积总和的写一个通用的用以求不同形状的图形面积总和的函数函数total()total()。class shape class shape public:public:virtual double area()const=0;virtual double area()const=0;47