《《c程序设计基础》第九章-多态性.ppt》由会员分享,可在线阅读,更多相关《《c程序设计基础》第九章-多态性.ppt(132页珍藏版)》请在三一办公上搜索。
1、C+程序设计基础,第9章 多态性北京邮电大学信通院方莉,2,多态性(Polymorphism)是面向对象程序设计的主要特征之一。多态性对于软件功能的扩展和软件重用都有重要的作用。是学习面向对象程序设计必须要掌握的主要内容之一。本章主要内容多态性的基本概念运行多态的实现,虚函数模板,第9章 多态性,3,第9章 多态性,4,9.1.1 面向对象程序设计中的多态,多态性是指同样的消息被不同类型的对象接收时导致完全不同的行为。消息:指示要调用类的某个成员函数。行为:成员函数执行的结果被视为对象的行为。,5,面向对象程序设计中多态性表现为以下几种形式:重载多态:通过调用相同名字的函数,表现出不同的行为。
2、运算符重载是一种重载多态。运行多态:通过基类的指针,调用不同派生类的同名函数(虚函数),表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。模板多态,也称为参数多态:通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。,9.1.1 面向对象程序设计中的多态,6,一个具有多态性的程序语句,在执行的时候,必须确定究竟是调用哪一个函数。确定具有多态性的语句究竟调用哪个函数的过程称为联编(Binding),有的资料也翻译成“绑定”。联编有两种方式:静态联编动态联编,9.1.2 多态的实现:联编,7,在源程序编译的时候就能确定具有多态性的语句调用哪个函
3、数,称为静态联编。对重载函数的调用就是在编译的时候确定具体调用哪个函数,所以是属于静态联编。模板在编译时确定被调函数,属于静态联编。,静态联编,8,动态联编则是在程序运行时,才能够确定具有多态性的语句究竟调用哪个函数。用动态联编实现的多态,也称为运行时的多态。虚函数是支持运行多态的基础。,动态联编,9,第9章 多态性,10,9.2.1 重载和静态联编,以下是一个重载函数的例子。int add(int a)return a+10;int add(int a,int b)return a+b;int main()int x=1,y=2;add(x);add(x,y);return 0;,11,9.
4、2.2 覆盖与静态联编,覆盖(Override)现象只能出现在继承树中。在派生类中定义和基类中同名的成员函数,是对基类进行改造,为派生类增加新的行为的一种常用的方法。在某些情况下,覆盖会导致静态联编;另外一些情况下,覆盖会导致动态联编。,12,9.2.2 覆盖与静态联编,通过派生类对象调用同名成员函数通过基类指针调用同名成员函数,13,一.通过派生类对象调用同名成员函数,在派生类中可以定义和基类的成员函数同名的成员函数。这是对基类进行改造,是派生类增加新的行为的一种常用的方法。在程序编译的时候,就可以确定派生类对象具体调用哪个同名的成员函数。这是通过静态联编实现的多态。,14,例 9-1 用派
5、生类对象访问继承树中的同名函数/以下代码仅为示例,无法编译class B public:f();/基类class P:public B public:f();/派生类1class Q:public B public:f();/派生类2void main()P p;/创建派生类P的对象 Q q;/创建派生类Q的对象p.f();/调用P:f()q.f();/调用Q:f(),一.通过派生类对象调用同名成员函数,15,例1:定义Circle类和Rectangle类为Shape类的派生类,通过Circle类和Rectangle类的对象调用重载函数getArea()显示对象的面积。/例1:shape.hc
6、lass Shape public:double getArea()const;void print()const;,一.通过派生类对象调用同名成员函数,16,class Circle:public Shape public:Circle(int=0,int=0,double=0.0);double getArea()const;/返回面积 void print()const;private:int x,y;/圆心 double radius;/半径;,一.通过派生类对象调用同名成员函数,17,class Rectangle:public Shape public:Rectangle(int=
7、0,int=0);double getArea()const;/面积void print()const;private:int a,b;/长和宽;,一.通过派生类对象调用同名成员函数,18,/例1:shape.cpp#include using namespace std;#include shape.h double Shape:getArea()const cout基类的getArea函数,面积是;return 0.0;void Shape:print()const coutBase class Objectendl;,一.通过派生类对象调用同名成员函数,19,Circle:Circle(
8、int xValue,int yValue,double radiusValue)x=xValue;y=yValue;radius=radiusValue;double Circle:getArea()const coutCircle类的getArea函数,面积是;return 3.14159*radius*radius;void Circle:print()const cout center is;coutx=x y=y;cout;radius is radiusendl;,一.通过派生类对象调用同名成员函数,20,Rectangle:Rectangle(int aValue,int bVa
9、lue)a=aValue;b=bValue;double Rectangle:getArea()const coutRectangle类的getArea函数,面积是;return a*b;void Rectangle:print()const cout hight is a;coutwidth isbendl;,一.通过派生类对象调用同名成员函数,21,/例1:chapter9_1.cpp#include using namespace std;#include shape.hvoid main()Circle circle(22,8,3.5);Rectangle rectangle(10,1
10、0);cout 调用的是;coutcircle.getArea()endl;/静态联编 cout 调用的是;coutrectangle.getArea()endl;/静态联编,调用的是Circle类的getArea函数,面积是38.4845 调用的是Ractangle类的getArea函数,面积是100,一.通过派生类对象调用同名成员函数,22,通过派生类对象调用同名成员函数,可以有以下的结论:派生类对象可以直接调用本类中与基类成员函数同名的函数,不存在二义性;在编译时就能确定通过对象将调用哪个函数,属于静态联编。,Circle circle(22,8,3.5);Rectangle recta
11、ngle(10,10);circle.getArea();rectangle.getArea();,一.通过派生类对象调用同名成员函数,23,二.通过基类指针调用同名成员函数,例 9-2 用指针访问继承树中的同名函数/以下代码仅为示例,无法编译class B public:f();/基类class P:public B public:f();/派生类1class Q:public B public:f();/派生类2void main()B*b_ptr;P p;Q q;/定义基类的指针和派生类的对象b_ptr=/通过基类指针调用B:f(),24,二.通过基类指针调用同名成员函数,从继承的角度来
12、看,派生类对象是基类对象的一个具体的特例。或者说,派生类对象是某一种特定类型的基类对象。例如,Circle类是Shape类的公有继承,“圆”是“图形”的一种特例。或者说,圆是一种特定的图形,具有图形的基本特征。,25,在关于基类对象和派生类对象的操作上,可以允许以下的操作:可以用派生类对象给基类对象赋值;例:Circle circle(22,8,3.5);Rectangle rectangle(10,10);Shape a=circle;Shape b=rectangle;,二.通过基类指针调用同名成员函数,26,可以用派生类对象初始化基类的引用。例:Shape 通过该指针,可以访问基类的公有
13、成员。,二.通过基类指针调用同名成员函数,27,以下这些操作是不能进行的:不能用基类对象给派生类对象赋值;例:Shape sp;Circle r=sp;/error不能用基类对象的地址初始化派生类对象的指针;例:Shape sp;Circle*pc=/error,二.通过基类指针调用同名成员函数,28,例2 在例1所定义的类的基础上,观察通过派生类对象地址初始化的基类对象的指针访问getArea函数的结果。#include using namespace std;#include shape.h void main()Shape*shape_ptr;Circle circle(22,8,3.5
14、);Rectangle rectangle(10,10);shape_ptr=/静态联编,circle 对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是 0rectangle 对象初始化shape_ptr指针访问的getArea函数是基类的getArea函数,面积是 0,29,程序运行结果表明:确实可以用派生类对象的地址初始化基类对象的指针;通过用派生类对象地址初始化的基类对象指针,只能调用基类的公有成员函数。在以上例子中,就是调用基类的getArea函数,而不是派生类的getArea函数。这种调用关系的确定,也是在编译的过程中完成的,属于静态联编。,
15、二.通过基类指针调用同名成员函数,30,2023/8/31,-30-,9.2.2 覆盖与静态联编,31,9.2.2 覆盖与静态联编,例9-3 修改例8-5,创建4个类,分别使用各个类的对象调用各类的同名函数,分别使用不同类的对象初始化基类指针,通过基类指针调用同名函数,观察同名函数调用结果。多文件结构:10个文件主函数Main03.cppGlobal03.h类TPoint的声明和定义:TPoint03.h和TPoint03.cpp类TShape的声明和定义:TShape03.h类TRect的声明和定义:TRect03.h类TCircle的声明和定义:TCircle03.h和TCircle03.
16、cpp类TEllipse的声明和定义:TEllipse03.h和TEllipse03.cpp,32,9.2.2 覆盖与静态联编,例9-3 重点:类层次结构:TShapeTRect,TShape TCircle TEllipse同名函数Draw();在主函数main中,通过各个类的对象调用Draw()函数,并且通过基类指针调用各个对象的Draw(),结果不同。,33,9.2.2 覆盖与静态联编,34,9.2.2 覆盖与静态联编,35,9.2.2 覆盖与静态联编,36,9.2.2 覆盖与静态联编,37,9.2.2 覆盖与静态联编,38,9.2.2 覆盖与静态联编,39,9.2.2 覆盖与静态联编,
17、40,第9章 多态性,41,9.3 虚函数与运行时的多态,在同名覆盖现象中,通过某个类的对象(对象指针、对象引用)调用同名函数,编译器会将该调用联编到该类的同名函数。通过基类对象指针(引用)是无法访问派生类普通函数的,即便这个指针(引用)是使用派生类对象初始化的。,42,9.3 虚函数与运行时的多态,通过指向基类的指针访问基类和派生类的同名函数,是实现运行时的多态的必要条件,但不是全部条件;如果希望利用基类的指针访问派生类的同名函数,必须将基类中的同名函数定义为虚函数。虚函数能够实现运行时的多态。,43,9.3.1 虚函数,在类的定义中声明虚函数,格式如下:virtual 返回值类型 函数名(
18、参数表);在函数原型中声明函数是虚函数后,具体定义这个函数时就不需要再说明它是虚函数了。在基类定义中直接定义虚函数的格式是:virtual 返回值类型 函数名(参数表)/函数体,44,9.3.1 虚函数,基类中的同名函数声明或定义为虚函数后,派生类的同名函数无论是不是用virtual来说明,都将自动地成为虚函数。从程序可读性考虑,一般都会在这些函数的声明或定义时,用virtual来加以说明。,45,2023/8/31,-45-,例 9-4 用指针+虚函数的形式实现动态联编class B public:virtual f();class P:public B public:f();class Q
19、:public B public:f();main()B*b_ptr;P p;Q q;b_ptr=/调用的是Q:f(),9.3.1 虚函数,46,2023/8/31,-46-,例 9-5 用引用+虚函数的形式实现动态联编class B public:virtual f();class P:public B public:f();class Q:public B public:f();main()P p;Q q;B/调用的是Q:f(),9.3.1 虚函数,47,例3:将例2进行修改,使得程序具有运行时的多态的效果。多文件结构 shape1.h:类的声明 shape1.cpp:类的定义和实现 ch
20、apter9_3.cpp:主函数,9.3.1 虚函数,48,例3:将例2进行修改,使得程序具有运行时的多态的效果。/例3:shape1.h#ifndef SHAPE_H#define SHAPE_Hclass Shape public:virtual double getArea()const;void print()const;,9.3.1 虚函数,49,class Circle:public Shape public:Circle(int=0,int=0,double=0.0);virtual double getArea()const;/double getArea()const;voi
21、d print()const;private:int x,y;double radius;,9.3.1 虚函数,50,class Rectangle:public Shape public:Rectangle(int=0,int=0);virtual double getArea()const;/double getArea()const;void print()const;private:int a,b;#endif,9.3.1 虚函数,51,/例3:shape1.cpp#include using namespace std;#include shape1.h double Shape:g
22、etArea()const cout基类的getArea函数,面积是;return 0.0;void Shape:print()constcoutBase class Objectendl;,9.3.1 虚函数,52,Circle:Circle(int xValue,int yValue,double radiusValue)x=xValue;y=yValue;radius=radiusValue;double Circle:getArea()const coutCircle类的getArea函数,面积是;return 3.14159*radius*radius;void Circle:pri
23、nt()const cout center is;coutx=x y=y;cout;radius is radiusendl;,9.3.1 虚函数,53,Rectangle:Rectangle(int aValue,int bValue)a=aValue;b=bValue;double Rectangle:getArea()const coutRectangle类的getArea函数,面积是;return a*b;void Rectangle:print()const cout hight is a;coutwidth isbendl;,9.3.1 虚函数,54,/例3:9_3.cpp#inc
24、lude using namespace std;#include shape1.h void main()Shape*shape_ptr;Circle circle(22,8,3.5);Rectangle rectangle(10,10);shape_ptr=,circle 对象初始化shape_ptr指针访问的getArea函数是Circle类的getArea函数,面积是 38.4845rectangle 对象初始化shape_ptr指针访问的getArea函数是Rectangle类的getArea函数,面积是 100,一种运行时决定的多态性,55,9.3.1 虚函数,一种运行时决定的多态
25、性,56,11.3.1 虚函数,要实现运行时的多态,需要以下条件:必须通过指向基类对象的指针访问和基类成员函数同名的派生类成员函数;shape_ptr=派生类的继承方式必须是公有继承;基类中的同名成员函数必须定义为虚函数。,57,9.3.1 虚函数,虚函数必须正确的定义和使用。否则,即使在函数原型前加了virtual的说明,也可能得不到运行时多态的特性。必须首先在基类中声明虚函数。在多级继承的情况下,也可以不在最高层的基类中声明虚函数。例如在第二层定义的虚函数,可以和第三层的虚函数形成动态联编。但是,一般都是在最高层的基类中首先声明虚函数。,58,9.3.1 虚函数,基类和派生类的同名函数,必
26、须函数名、返回值、参数表全部相同,才能作为虚函数来使用。否则,即使函数用virtual来说明,也不具有虚函数的行为。静态成员函数不可以声明为虚函数。构造函数也不可以声明为虚函数。析构函数可以声明为虚函数,即可以定义虚析构函数。,59,例4 虚函数的正确使用。分析以下程序,编译时哪个语句会出现错误?为什么?将有错误的语句屏蔽掉以后,程序运行结果如何?其中哪些调用是静态联编,哪些是动态联编?#include class BBpublic:virtual void vf1()coutBB:vf1被调用n;virtual void vf2()coutBB:vf2被调用n;void f()coutBB:
27、f被调用n;class DD:public BBpublic:virtual void vf1()coutDD:vf1被调用n;void vf2(int i)coutiendl;void f()coutDD:fn被调用;,60,void main()DD d;BB*bp=,将这个语句注释掉后,运行结果将显示:DD:vf1被调用BB:vf2被调用BB:f被调用,函数调用bp-vf2(10);是错误的。因为派生类的vf2函数和基类的vf2函数的参数不同,派生类的vf2就不是虚函数。,其中bp-vf1()调用是动态联编。bp-vf2()是静态联编。bp-f()也是静态联编。,9.3.1 虚函数,61
28、,9.3.2 虚析构函数,如果用动态创建的派生类对象的地址初始化基类的指针,创建的过程不会有问题:仍然是先调用基类构造函数,再执行派生类构造函数。但是,在用delete运算符删除这个指针的时候,由于指针是指向基类的,通过静态联编,只会调用基类的析构函数,释放基类成员所占用的空间。而派生类成员所占用的空间将不会被释放。,62,#include using namespace std;class Shape public:Shape()coutShape类构造函数被调用n;Shape()coutShape类析构函数被调用n;class Circle:public Shape public:Circ
29、le(int xx=0,int yy=0,double rr=0.0)x=xx;y=yy;radius=rr;coutCircle类构造函数被调用n;Circle()coutCircle类析构函数被调用n;private:int x,y;double radius;void main()Shape*shape_ptr;shape_ptr=new Circle(3,4,5);delete shape_ptr;,例9.5 定义简单的Shape类和Circle类,观察基类指针的创建和释放时如何调用构造函数和析构函数。,程序运行后在屏幕上显示:Shape类构造函数被调用Circle类构造函数被调用Sh
30、ape类析构函数被调用,63,#include using namespace std;class Shape public:Shape()coutShape类构造函数被调用n;virtual Shape()coutShape类析构函数被调用n;class Circle:public Shape public:Circle(int xx=0,int yy=0,double rr=0.0)x=xx;y=yy;radius=rr;coutCircle类构造函数被调用n;Circle()coutCircle类析构函数被调用n;private:int x,y;double radius;void ma
31、in()Shape*shape_ptr;shape_ptr=new Circle(3,4,5);delete shape_ptr;,例9.5 定义简单的Shape类和Circle类,观察基类指针的创建和释放时如何调用构造函数和析构函数。,程序运行后在屏幕上显示:Shape类构造函数被调用Circle类构造函数被调用Circle类析构函数被调用Shape类析构函数被调用,64,为了解决派生类对象释放不彻底的问题,必须将基类的析构函数定义为虚析构函数。格式:virtual Shape();此后,无论派生类析构函数是不是用virtual来说明,也都是虚析构函数。再用delete shape_ptr来
32、释放基类指针时,就会通过动态联编调用派生类的析构函数。,9.3.2 虚析构函数,65,第9章 多态性,66,9.4 纯虚函数和抽象类,前例,基类Shape并不是一个具体的“形状”的抽象,而是各种实际的“形状”的抽象。在C+中,对于那些在基类中不需要定义具体的行为的函数,可以定义为纯虚函数。对于那些只是反映一类事物公共特性的类,在C+中可以定义为“抽象类”。凡是带有一个或几个纯虚函数的类,就是抽象类。,67,9.4 纯虚函数和抽象类,纯虚函数格式:virtual 返回值类型 函数名(参数表)=0;,68,9.4 纯虚函数和抽象类,纯虚函数的声明和使用有以下的特点:纯虚函数一定是在基类中声明的。在
33、多级继承的情况下,纯虚函数除了在最高层基类中声明外,也可以在较低层的基类中声明。纯虚函数是没有函数体的。函数体是用“=0”来代替了。纯虚函数是不可以被调用的。凡是需要被调用的函数都不可以声明为纯虚函数。,69,9.4 纯虚函数和抽象类,包含纯虚函数的类为抽象类。抽象类定义的一般形式是:class 类名public:virtual 返回值类型 函数名(参数表)=0;/其他函数的声明;,70,9.4 纯虚函数和抽象类,抽象类的定义和使用具有以下的特点:不可以定义抽象类的对象。可以定义抽象类的指针和抽象类的引用。目的是通过这些指针或引用访问派生类的虚函数,实现运行时的多态。如果抽象类的派生类中没有具
34、体实现纯虚函数的功能,这样的派生类仍然是抽象类。抽象类中除了纯虚函数外,还可以定义其他的非纯虚函数。,71,/例6:shape2.h#ifndef SHAPE_H#define SHAPE_Hclass Shape/基类Shape的定义,抽象类public:virtual double getArea()const=0;/纯虚函数void print()const;virtual Shape()/虚析构函数;,9.4 纯虚函数和抽象类,72,class Circle:public Shape public:Circle(int=0,int=0,double=0.0);double getAre
35、a()const;/返回面积void print()const;/输出Circle 类对象tprivate:int x,y;/圆心座标double radius;/圆半径;,9.4 纯虚函数和抽象类,73,class Rectangle:public Shape public:Rectangle(int=0,int=0);/构造函数double getArea()const;/返回面积void print()const;/输出Rectangle类对象private:int a,b;/矩形的长和宽;#endif,9.4 纯虚函数和抽象类,74,/例6:shape2.cpp#include usi
36、ng namespace std;#include shape2.h void Shape:print()constcoutBase class Objectendl;Circle:Circle(int xValue,int yValue,double radiusValue)x=xValue;y=yValue;radius=radiusValue;,75,double Circle:getArea()const coutCircle类的getArea函数,面积是;return 3.14159*radius*radius;void Circle:print()const cout center
37、 is;coutx=x y=y;cout;radius is radiusendl;,76,Rectangle:Rectangle(int aValue,int bValue)a=aValue;b=bValue;double Rectangle:getArea()const coutRectangle类的getArea函数,面积是;return a*b;void Rectangle:print()const cout hight is a;coutwidth isbendl;,77,/例6:9_6.cpp#include using namespace std;#include shape2.
38、h void creat_object(Shape*ptr);void display_area(Shape*ptr);void delete_object(Shape*ptr);void main()Shape*shape_ptr;creat_object(,78,void creat_object(Shape*ptr)/二级指针将在子函数中申请的地址带回char type;*ptr=NULL;do couttype;switch(type)case c:int xx,yy;double rr;coutxxyyrr;*ptr=new Circle(xx,yy,rr);break;case r
39、:int aa,bb;coutaabb;*ptr=new Rectangle(aa,bb);break;default:cout类型错误,请重新选择n;while(*ptr=NULL);,79,void display_area(Shape*ptr)coutgetArea()endl;void delete_object(Shape*ptr)delete ptr;,创建对象。c:Circle类对象;r:Rectangle类对象c请输入圆心的座标和圆的半径:1 1 5显示所创建对象的面积,调用的是Circle类的getArea函数,面积是78.54,80,这个程序中,使用了本章所介绍的虚函数、纯
40、虚函数、虚析构函数、基类指针访问派生类对象等技术,实现了运行时的多态。程序具有很好的可扩展性。如果需要增加新的派生类,当然要增加和派生类定义有关的代码,和创建对象所需要的语句。但是,对于函数display_area,display_object等函数 都不用修改。,9.4 纯虚函数和抽象类,81,这个例子还显示了:抽象类中可以为各派生类定义一些通用的接口。这些通用的接口就是抽象类中的纯虚函数。新增加的派生类的对象,都可以使用这样的通用接口,表现派生类对象的行为特性。,9.4 纯虚函数和抽象类,82,例7:在例6的基础上,现在需要增加一个Rectangle的派生类:Cube,也就是立方体类。任意
41、创建Circle、Rectangle、Cube类的对象,并显示对象的面积。需要做的只有两件事:定义新的Cube类;在create_object函数中增加创建Cube类对象的内容。其他函数都不需要修改。,9.4 纯虚函数和抽象类,83,/例7:cube.h#include“shape2.h”#ifndef CUBE_H#define CUBE_Hclass Cube:public Rectangle public:Cube(int,int,int);double getArea()const;void print()const;private:int c;#endif,9.4 纯虚函数和抽象类,
42、84,/例7:cube.cpp#include using namespace std;#include cube.h Cube:Cube(int aValue,int bValue,int cValue):Rectangle(aValue,bValue)c=cValue;double Cube:getArea()const coutCube类的getArea函数,还要调用;return Rectangle:getArea()*c;void Cube:print()const Rectangle:print();cout“high is cendl;,9.4 纯虚函数和抽象类,85,/9_7.
43、cpp#include using namespace std;#include shape2.h#include cube.hvoid creat_object(Shape*ptr);void display_area(Shape*ptr);void delete_object(Shape*ptr);void main()Shape*shape_ptr;creat_object(,9.4 纯虚函数和抽象类,86,void creat_object(Shape*ptr)char type;*ptr=NULL;do couttype;switch(type)case c:/创建Ciecle类对象
44、 int xx,yy;double rr;coutxxyyrr;*ptr=new Circle(xx,yy,rr);break;,87,case r:/创建Rectangle类对象 int aa,bb;coutaabb;*ptr=new Rectangle(aa,bb);break;case u:/创建Cube类对象 int aa,bb,cc;coutaabbcc;*ptr=new Cube(aa,bb,cc);break;default:cout类型错误,请重新选择n;while(*ptr=NULL);,Creat_object函数多增加了一个分支,88,程序执行结果:,创建对象。请选择:c
45、:Circle类对象;r:Rectangle类对象;u:Cube类对象u请输入立方体的长、宽、高:4 5 6显示所创建对象的面积,调用的是Cube类的getArea函数,还要调用 Rectangle类的getArea函数,面积是 120,9.4 纯虚函数和抽象类,89,第9章 多态性,90,2023/8/31,-90-,9.5 模板,模板是C+中的通用程序模块。在这些程序模块中有一些数据类型是不具体的,或者说是抽象的。当这些抽象的数据类型更换为不同的具体数据类型以后,就会产生一系列具体的程序模块。C+中的模板包括函数模板和类模板。函数模板实例化后产生的函数,称为模板函数。类模板实例化后产生的类
46、,称为模板类。,91,2023/8/31,-91-,9.5.1 函数模板,1.函数模板定义的基本格式:template 函数返回类型 函数名(形式参数列表)函数体“template”是定义模板的关键字。在一对尖括号内,关键字“typename”后面声明所使用的“参数化类型名”。关键字“typename”可以用“class”取代。,92,2023/8/31,-92-,9.5.1 函数模板,模板的其余部分和一般的函数定义的格式完全相同。只是在函数定义时可以使用参数化类型来代表各种具体的数据类型。参数化类型可以用于:函数返回值类型;函数参数表内形式参数的类型;函数体内,自动变量的类型。,93,202
47、3/8/31,-93-,例 9-10 函数模板的定义和使用:定义并使用“求3个数最大值”的函数模板。/Main10.cpp#includeusing namespace std;templateT max_value(T x,T y,T z)/函数模板的定义:求x、y、z的最大值 T temp;temp=xy?x:y;return tempz?temp:z;int main()coutmax_value(12,32,21)endl;/用整数作实参调用函数模板 coutmax_value(a,A,9)endl;/用字符作实参调用函数模板 return 0;,程序执行后,输出:32a,9.5.1
48、函数模板,94,2023/8/31,-94-,9.5.1 函数模板,2.函数模板定义的一般格式 template 函数返回类型 函数名(形式参数列表)函数体,95,2023/8/31,-95-,例 9-11 编写一个函数模板,可以按指定的操作数类型进行乘法。/Main11.cpp#includeusing namespace std;template P1 cal(P1 x,P2 y)/函数模板有两个参数化类型名:P1和P2 return(x*static_cast(y);/按x的数据类型执行乘法void main()unsigned short w=230;short z=150;coutc
49、al(w,z)endl;/按无符号数相乘 coutcal(z,w)endl;/按有符号数相乘,程序执行后,输出:34500-31036,96,有符号的运算,230*150=34500=32768+1024+512+128+64+4-(214+213+212+211+28+25+24+23+22)=-31036,97,2023/8/31,-97-,9.5.1 函数模板,3.带有确定类型的参数的函数模板 函数模板的形式参数表中除了使用参数化类型名以外,还可以使用确定类型的参数。函数模板的参数表中,一定要包含参数化类型名,但不一定都使用参数化类型名。根据需要,可以使用确定类型的参数。如整型的参数、实
50、型的参数,等等。,98,2023/8/31,-98-,例 9-12 设计和编写一个通用的输入数组元素的函数模板。可以用它来输入各种不同数据类型的数组。/例9-12。包含1个文件 Main12.cpp/Main12.cpp#include#include using std:cout;using std:cin;using std:endl;template/函数模板void ArrayInput(Q1 array,int num)cout arrayj;/输入数组元素int main()int number;float floatArray4;int intArray3;number=size