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