面向对象程序设计PPT课件.ppt
面向对象程序设计面向对象程序设计第第5 5章章 多态与抽象类多态与抽象类n 5.1 5.1 类层次的多态问题类层次的多态问题n 5.2 5.2 类型兼容规则类型兼容规则( (重点重点) )n 5.3 5.3 多态的概念多态的概念n 5.4 5.4 多态的分类多态的分类n 5.5 5.5 联编联编n 5.6 5.6 虚函数虚函数( (重点重点) )n 5.7 5.7 抽象类与纯虚函数抽象类与纯虚函数( (重点重点) )第5章多态与抽象类2023-4-43n【例【例5-15-1】在例】在例4-14-1程序中存在的两个程序中存在的两个不足:不足:(1 1)基类)基类Pay()Pay()和和Display()Display()的函数体均为空,的函数体均为空,在实现部分仍要写出函数体,显得冗余。在实现部分仍要写出函数体,显得冗余。(2 2)在主函数中,建立了)在主函数中,建立了4 4个不同类的对象,个不同类的对象,进行了类似的操作,重复写了进行了类似的操作,重复写了4 4遍类似的语句遍类似的语句,程序不够简洁。,程序不够简洁。第5章多态与抽象类2023-4-44n基类设置成员函数基类设置成员函数Pay()Pay()和和Display()Display()的目的是为了统一规定类簇的基本行的目的是为了统一规定类簇的基本行为,虽有冗余,但也必要。为此,为,虽有冗余,但也必要。为此,C+C+提供了纯虚函数来解决此问题。提供了纯虚函数来解决此问题。 第5章多态与抽象类2023-4-45n在主函数中建立了在主函数中建立了4 4个不同类的对象,进行个不同类的对象,进行了类似的操作,重复写了了类似的操作,重复写了4 4遍类似的语句。遍类似的语句。n应该怎样有效地来处理它们同样的行为呢应该怎样有效地来处理它们同样的行为呢?我们首先想到的是用循环解决,但调用?我们首先想到的是用循环解决,但调用这些同名函数的对象却不相同。这些同名函数的对象却不相同。n分析这些对象有一个共同点,那就是来自分析这些对象有一个共同点,那就是来自于同一个基类,而基类与派生类对象有什于同一个基类,而基类与派生类对象有什么关系呢?事实上,它们遵循类型兼容规么关系呢?事实上,它们遵循类型兼容规则。则。第5章多态与抽象类2023-4-46Employee Employee * *emp4=&m1,&t1,&s1,&sm1; /emp4=&m1,&t1,&s1,&sm1; /声明声明抽象类指针数组抽象类指针数组for(int i=0;i4;i+)for(int i=0;iPay(); /empi-Pay(); /单一指令,实现多态,单一指令,实现多态,计算指针指向对象的工资计算指针指向对象的工资empi-Display();/empi-Display();/单一指令,实现多态,单一指令,实现多态,输出指针指向对象的信息输出指针指向对象的信息 第5章多态与抽象类2023-4-47n类型兼容规则是指在需要基类对象的类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的任何地方,都可以使用公有派生类的对象来替代。对象来替代。第5章多态与抽象类2023-4-48n通过公有继承,派生类得到了基类中通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成除构造函数、析构函数之外的所有成员。这样,员。这样,公有派生类实际就具备了公有派生类实际就具备了基类的所有功能基类的所有功能,凡是基类能解决的,凡是基类能解决的问题,公有派生类都可以解决。问题,公有派生类都可以解决。 第5章多态与抽象类2023-4-49n类型兼容规则中所指替代包括以下情类型兼容规则中所指替代包括以下情况:况:(1 1)派生类的对象可以赋值给基类的对象。)派生类的对象可以赋值给基类的对象。(2 2)派生类的对象可以初始化基类的引用。)派生类的对象可以初始化基类的引用。(3 3)派生类的对象的地址可以赋值给基类的指)派生类的对象的地址可以赋值给基类的指针变量。针变量。n在替代之后,派生类对象就可以作为在替代之后,派生类对象就可以作为基类的对象使用,但只能访问从基类基类的对象使用,但只能访问从基类继承的成员。继承的成员。第5章多态与抽象类2023-4-410n【例【例5-35-3】示例类型兼容规则的应用。】示例类型兼容规则的应用。n教材教材P261P261,该程序的运行结果为:,该程序的运行结果为:Base classBase classBase classBase classBase classBase classDerivel classDerivel classDerive2 class Derive2 class 第5章多态与抽象类2023-4-411n根据类型兼容规则,基类指针根据类型兼容规则,基类指针p p可以指可以指向派生类对象向派生类对象obj2obj2、obj3obj3(即派生类(即派生类对象可以代替基类对象,给基类指针对象可以代替基类对象,给基类指针赋值),但编译时,编译器根据赋值),但编译时,编译器根据p p的类的类型是基类型,自动调用基类的型是基类型,自动调用基类的who()who(),所以结果如上所示。所以结果如上所示。 第5章多态与抽象类2023-4-412n【思考题【思考题5-35-3】如果基类指针要访问派】如果基类指针要访问派生类的生类的who()who(),怎么办?,怎么办?n nC+C+提供了多态机制来解决这个问题。提供了多态机制来解决这个问题。n类型兼容规则是类型兼容规则是C+C+多态的重要基础。多态的重要基础。 第5章多态与抽象类2023-4-413n多态(多态(PolymorphismPolymorphism)是指具有相似)是指具有相似功能的不同函数使用同一个名称来实功能的不同函数使用同一个名称来实现,从而可以使用相同的调用方式来现,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数的调用这些具有不同功能的同名函数的特性。特性。 第5章多态与抽象类2023-4-414nC+C+支持的多态可以分为四种类型:支持的多态可以分为四种类型:重载多态:函数重载和运算符重载重载多态:函数重载和运算符重载强制多态:强制类型转换强制多态:强制类型转换包含多态:虚函数包含多态:虚函数参数多态:函数模板和类模板参数多态:函数模板和类模板 第5章多态与抽象类2023-4-415n联编是指把一个标识符名和一个存储联编是指把一个标识符名和一个存储地址联系在一起的过程。即函数调用地址联系在一起的过程。即函数调用与某个函数在多态的实现过程中,确与某个函数在多态的实现过程中,确定调用哪个同名联系的过程,又称绑定调用哪个同名联系的过程,又称绑定。分为:定。分为:n静态联编静态联编n动态联编动态联编第5章多态与抽象类2023-4-416n静态联编是在编译阶段完成的联编。静态联编是在编译阶段完成的联编。例例5-25-2、例、例5-35-3及以前的函数重载都是及以前的函数重载都是采用静态联编方式。采用静态联编方式。n例例5-45-4,教材,教材P265P265,是静态联编。,是静态联编。n动态联编是在运行阶段完成的联编。动态联编是在运行阶段完成的联编。 第5章多态与抽象类2023-4-417n在例在例5-45-4中,静态联编把基类指针中,静态联编把基类指针psps指指向的对象绑定到基类上,而在运行时向的对象绑定到基类上,而在运行时进行动态联编将把进行动态联编将把psps指向的对象绑定指向的对象绑定到派生类上。到派生类上。第5章多态与抽象类2023-4-418n可见,同一个指针,在不同阶段被绑可见,同一个指针,在不同阶段被绑定的类对象将是不同的,进而被关联定的类对象将是不同的,进而被关联的类成员函数也是不同的。的类成员函数也是不同的。n如何来确定是用静态联编还是用动态如何来确定是用静态联编还是用动态联编呢?联编呢?nC+C+规定,动态联编通过继承和虚函数规定,动态联编通过继承和虚函数来实现。来实现。第5章多态与抽象类2023-4-419n虚函数是动态联编的基础。下面介绍虚函数是动态联编的基础。下面介绍虚函数。虚函数。第5章多态与抽象类2023-4-420n虚函数就是在基类中被关键字虚函数就是在基类中被关键字virtualvirtual说明、并在一个或多个派生类中被重说明、并在一个或多个派生类中被重新定义的成员函数。新定义的成员函数。第5章多态与抽象类2023-4-421n声明虚函数的格式如下:声明虚函数的格式如下:virtual virtual (););第5章多态与抽象类2023-4-422n在派生类中重新定义虚函数时,其函在派生类中重新定义虚函数时,其函数原型包括返回类型、函数名、参数数原型包括返回类型、函数名、参数个数与参数类型的顺序,都必须与基个数与参数类型的顺序,都必须与基类中的原型必须相同。类中的原型必须相同。第5章多态与抽象类2023-4-423n一个函数一旦被声明为虚函数,则无一个函数一旦被声明为虚函数,则无论声明它的类被继承了多少层,在每论声明它的类被继承了多少层,在每一层派生类中该函数都保持虚函数特一层派生类中该函数都保持虚函数特性。因此,在派生类中重新定义该函性。因此,在派生类中重新定义该函数时,可以省略关键字数时,可以省略关键字virtualvirtual。但是。但是,为了提高程序的可读性,往往不省,为了提高程序的可读性,往往不省略。略。第5章多态与抽象类2023-4-424n在程序运行时,不同类的对象调用各在程序运行时,不同类的对象调用各自的虚函数,这就是运行时多态。自的虚函数,这就是运行时多态。第5章多态与抽象类2023-4-425n【例【例5-55-5】将例】将例5-45-4基类的成员函数基类的成员函数print()print()设为虚函数,采用对象指针调设为虚函数,采用对象指针调用虚函数,进而实现动态联编。用虚函数,进而实现动态联编。 第5章多态与抽象类2023-4-426n该程序的运行结果为该程序的运行结果为: :A studentA studentA graduate studentA graduate studentA studentA studentA studentA studentA graduate studentA graduate student第5章多态与抽象类2023-4-427n【思考题【思考题5-55-5】如果将例】如果将例5-55-5中中StudentStudent类改类改为:为:class Studentclass Student public:public: virtual void print(); virtual void print(); /虚函数的声明虚函数的声明;virtual void Student:print() /virtual void Student:print() /虚函数的实现虚函数的实现 coutA studentendl; coutA studentendl; 第5章多态与抽象类2023-4-428n注意:当有虚函数声明时,注意:当有虚函数声明时,virtualvirtual关关键字只用在虚函数的声明中,不能用键字只用在虚函数的声明中,不能用在虚函数定义中。在虚函数定义中。 第5章多态与抽象类2023-4-429n【思考题【思考题5-45-4】将例】将例5-55-5中用中用对象对象调用调用虚函数,其结果如何?虚函数,其结果如何?n【注意】只有通过对象指针或对象引【注意】只有通过对象指针或对象引用来调用虚函数,才能实现动态联编用来调用虚函数,才能实现动态联编。如果采用对象来调用虚函数,则采。如果采用对象来调用虚函数,则采用的是静态联编方式。用的是静态联编方式。第5章多态与抽象类2023-4-430n【例【例5-65-6】将例】将例5-45-4基类的成员函数基类的成员函数print()print()设为虚函数,采用对象引用调设为虚函数,采用对象引用调用虚函数,进而实现动态联编。用虚函数,进而实现动态联编。 第5