C++程序设计第7章 函数模板教学课件.ppt
《C++程序设计第7章 函数模板教学课件.ppt》由会员分享,可在线阅读,更多相关《C++程序设计第7章 函数模板教学课件.ppt(39页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C+程序设计第7章 函数模板教学课件C C语言程序设计案例教程语言程序设计案例教程语言程序设计案例教程语言程序设计案例教程第7章 函数模板所谓函数模板,实际上是建立一个通用函数,用它对逻辑功能相同,但数据类型不同的一组函数进行统一描述,而其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。利用函数模板可以用一种逻辑过程处理不同类型的数据,从而提高编程的效率。http:/ 目录 7.1 定义函数模板 7.2 使用函数 7.4 函数模板的其他知识点3http:/ 7.1 定义函数模板定义函数模板与定义函数是相似的,其目的都是定义一个算法逻辑,只是一般函数的定义给出了具体的返回值类型和参数类型,
2、而函数模板的定义使用的则是虚拟的类型名,也即将其使用的类型进行了参数化。一般的函数往往只能处理一种数据类型,函数模板是为处理多种数据类型而定义的。7.1.1 使用函数模板的必要性在实际编程中,可能会遇到这样的问题:定义一个简单的求数组中最大值的函数,要求可以处理各种数据类型。学习了函数重载(4.7节)后,我们可能希望定义几个这样的函数,每一个可以比较一种给定类型的值,即第一次尝试可能是定义几个重载函数。4http:/ 3这里只写出了针对int型和float型数组的函数。为了适应各种情形,还应当编写针对double、char、short、long等数据类型的函数。除此之外,还要支持自定义数据类型
3、(如结构体、类等)。可见,这种面面俱到的方式虽能解决问题,但真正实施起来,不仅麻烦而且容易出错。一方面,由于每个要比较的类型都需要重复函数的函数体,程序的代码量会大大增加;另一方面,一旦算法逻辑发生改变,或者要纠正某个错误,所有的重载函数都需要作相同的修改。例如:int max(int ary,int n)/整型数组求最大值 int max_val=0;/保存最大值的变量 for(int i=0;iaryi?max_val:aryi;/记录最大值return max_val;/返回最大值float max(float ary,int n)/float型数组求最大值 float max_val=
4、0.0;/以下的逻辑过程同上面求整型数组中最大值的逻辑一致 for(int i=0;iaryi?max_val:aryi;/记录最大值return max_val;/返回最大值http:/ 经过分析可知,这些函数在算法逻辑上都是一样的,即每个函数的函数体是相同的。它们之间唯一的区别是所处理数据的类型不同。鉴于这种情况,C+语言提供了函数模板。函数模板将函数的类型进行参数化处理,使得只编写一次通用算法逻辑就可以处理所有的数据类型,而不必为每个类型定义一个新函数。例如,上面的例子就可以用函数模板改写为如下的形式(如何定义并使用函数模板将在后面的章节中讲解)。template /定义函数模板,T是类
5、型变量,代表某种类型T max(T ary,int n)/使用类型T定义函数的返回值和参数类型 T max_val=0;/使用类型T声明临时变量 for(int i=0;i aryi?max_val:aryi;/记录最大值return max_val;/返回最大值可以看到,通过使用函数模板,避免了相同逻辑的无意义重复,简化了程序,同时也减轻了程序设计者维护程序的工作量,提高了开发效率。5http:/ 7.1.2 抽取通用算法逻辑定义函数模板,通常是出于这样的目的:定义一个适用于各种数据类型的通用算法。例如,定义一个表示两个数相减的函数,要求传入两个任意类型的参数,最终返回这两个参数的差。由前面
6、的介绍可知,利用函数模板,对函数类型进行参数化处理就能达到目的。但事实上,函数类型(即返回类型和参数类型)并不是问题的核心,问题的关键是减法(算法)。只要对常见的数据类型,如整型、浮点型等,都使用减法运算符“-”即可。所以这里的通用算法逻辑就是两个参数相减,并返回结果。抽取通用算法逻辑的过程如图7-1所示。6图7-1 抽取通用算法逻辑图7-1是以类型作为模板参数的,实际上,常量也可以作为模板参数,例如:template /定义函数模板,T是类型变量,size为非类型参数这里将数组的大小size作为模板参数。http:/ 7.1.3 函数模板定义的语法在C+中,函数模板的定义以关键字templa
7、te开始,接着在符号“”之间给出函数的模板参数列表,该列表不能为空。模板参数可以是表示类型的类型参数,也可以是表示常量表达式的非类型参数。定义函数模板的语法如下。template 返回类型 函数名(函数参数列表)函数体例如,前面求数组中最大值的函数模板的定义如下。template T max(T ary,int n)7http:/ 1)模板形参模板参数列表中可以包含一个或多个类型参数,也可以是一个或多个非类型参数,甚至可以是两种参数的混合。只是在有多个模板参数时,必须用逗号隔开,并且该列表不能为空。非类型参数跟在类型说明符之后,就像声明普通参数一样声明;类型参数跟在关键字typename或cl
8、ass之后定义。例如:template 上述模板参数列表中就声明了两个类型参数和两个非类型参数。其中,两个类型参数T1和T2,代表两种数据类型,可以是自定义类型,也可以是内置类型;两个非类型参数num1和num2,其声明表示该模板在定义过程中可以将它们当做常量使用。注意:类型参数也是一个标识符,应当遵循标识符的命名规则,即只能由数字、字母和下划线组成,并且不能以数字开头。关键字typename和关键字class的作用相同,都是表示它后面的参数名代表一个潜在的内置或用户自定义的类型,二者可以互换。但这里的class容易与类的声明(将在9.1.1中介绍)关键字class混淆。虽然它们都由相同的字母
9、组成,但却代表不同的含义。为了区别类与模板参数中的类型关键字class,标准C+提出了用typename作为模板参数的类型关键字,同时也支持使用class。9http:/ 如果用typename,其含义就很清楚,肯定是类型名而不是类名。在函数参数列表以及函数体的定义过程中,可以使用模板参数列表中的参数。如果使用类型参数,可以用该类型声明函数的返回值、形参、临时变量,例如:template /定义函数模板,类型参数TT max(T ary,int n)/使用类型参数T声明函数的返回值、形参类型 函数体如果使用非类型参数,那么可以在函数体的定义中将其当做常量使用,也可以将其当做函数的默认实参,例如
10、:template /定义函数模板,非类型参数sizeT max(T ary,int n=size)/使用非类型参数size,将size默认为数组的长度 函数体10http:/ 2)参数化的函数定义模板参数列表之后紧接着就是一个参数化的函数定义。这里所谓的参数化,主要是指对函数返回值类型、形参类型以及函数体中常量类型的参数化。例如,定义一个求数组中最大值的函数模板,对于不同的数据类型(如整型、浮点型)其算法逻辑都是一样的,不一样的只是函数返回值及参数的类型,因此可以将这两部分参数化。【例7-1】用函数模板求数组中的最大值。#include using namespace std;templat
11、e /定义函数模板,T为类型参数T max(T ary,int n)/T型数组求最大值 T max_val=0;/保存最大值的变量 for(int i=0;imax_val)/如果当前元素比当前最大值大 max_val=aryi;/修改保存最大值变量return max_val;/返回最大值void main()/理解函数模板11http:/ int a14=2,8,4,6;/定义整型数组并初始化 int i=max(a1,4);/调用函数模板,求整型数组的最大值,此时T被int取代 double a24=2.0,1.2,4.1,3.4;/定义浮点型数组并初始化 double d=max(a2
12、,4);/调用函数模板,此时T被double取代 cout i_max=i endl;cout d_max=d endl;【例7-1】的第一行定义了一个函数模板,类型参数T。在第二行的函数模板max的定义中,第一个T是函数返回值类型的参数,第二个T和后面的ary 表示T类型的数组形参。第四行的T用来在函数体中定义一个类型为T的变量。T是max模板的模板参数,可以用来表示多种数据类型,只是此时还未确定,要到模板max被使用时才能确定。编译调试,程序运行结果如图7-2所示。图7-2 利用函数模板求数组中的最大值12http:/ 12需要说明如下几点。需要说明如下几点。(1)在定义函数模板时,不允许
13、template语句与参数化的函数(函数模板)之间有任何其他语句。下面的模板定义是错误的。template int i;/错误,不允许在此位置有任何语句 T max(T ary,int n)(2)模板的类型参数T与具体类型的关系有点像变量和变量值的关系。T是一个类型变量,其类型就是“类型”,而其值就是某种具体类型,如int、double等。(3)参数名T由程序员定义,其实也可以不用T而用任何一个标识符,许多人习惯用T(T是Type的第一个字母),而且用大写,以与实际的类型名相区别。(4)可以用与非模板函数一样的方式声明函数模板为inline。只是inline说明符应放在模板形参表之后、返回类型
14、之前,不能放在关键字template之前。例如:template inline T max(T,int);/正确inline template T max(T,int);/错误http:/ 7.1.4 使用非类型参数前面已经提到,模板参数可以是一个类型参数,代表一种类型;也可以是一个非类型参数,代表一个常量。使用非类型参数的目的就是为函数引入一个常量,以便在定义函数时当做常量或默认实参使用。例如,对于求一个数组的最大值问题,可以用一个非类型参数表示数组长度。下面的代码就是使用非类型参数来定义函数模板。template /定义函数模板,类型参数为T,非类型参数为sizeT max(T ary,i
15、nt n=size)/接受类型为T的数组及默认为size的数组长度 T max_val=0;/定义T类型的变量max_val for(int i=0;imax_val)/如果当前元素比最大值还大 max_val=aryi;/修改最大值return max_val;/返回最大值程序的第一行就定义了一个非类型参数size,用以表示数组的长度。注意:C+标准规定模板参数必须在程序编译时确定。因此非类型的模板参数的值必须是一个在程序编译时就能确定的常量(包括字面常量、const符号常量等)13http:/ 7.2 使用函数由于函数模板不是真正的函数,而只是对逻辑功能相同但数据类型不同的一组函数的统一描
16、述,因此使用函数模板不是简单的函数调用。在使用函数模板之前,必须先将模板的参数替换为具体的数据类型。在C+中,函数模板参数的替换可以由用户明确指定,也可以由编译器自行推演。7.2.1 实例化函数模板在程序中使用函数模板,实际上是使用该模板的实例。在程序编译过程中,编译器根据函数调用中提供的函数实参类型推演出具体的模板参数,然后用这些模板参数实例化模板,并将产生的实例编译成具体的机器码。一旦编译器确定了实际的模板参数,我们就称它实例化了函数模板的一个实例实质上,编译器承担了为我们使用的各种类型而编写函数的单调、重复工作。编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。例如
17、,对于如下所示的函数模板:template T max(T arysize)T max_val=ary0;for(int i=1;imax_val)/如果当前元素比最大值还大 max_val=aryi;/修改最大值14http:/ return max_val;/返回最大值如果在main函数中有以下语句:int main()int i_ary4=8,5,12,3;int i=max(i_ary);double d_ary6=12.3,9.8,13.4,6.7,26.0,5.3;double d=max(d_ary);编译器将实例化函数模板max的两个不同的版本,它将用int代替T创建第一个版本
18、,并用double代替T创建第二个版本。在上述代码中,main函数第三行定义了一个含4个元素的整型数组,当程序执行到第四行时,编译器根据函数max的实参决定模板实参size的值和T的类型。分别是4和int。同理,main函数第五行定义了一个含6个元素的浮点型数组,当程序执行到第六行时,编译器又会根据函数max的实参决定模板实参size的值和T的类型。分别是6和double。只有当编译器遇到程序中对函数模板的调用时,它才会根据调用语句中实参的具体类型,确定模板参数的数据类型和参数值,并用此类型和相应的值替换函数模板中的模板参数,生成能够处理该类型的函数代码,即模板实例。16http:/ 16图7
19、-3 函数模板与模板函数的关系为帮助理解,下面定义一个函数模板,用于查找一个数组中的某个值。如果找到了,就返回该值在数组中的位置;如果找不到,则返回-1。http:/ 17【例7-2】实例化函数模板。#include using namespace std;template /定义函数模板,包括类型参数T和数值参数sizeint search(T(&ary)size,T ele)/定义查找函数:在T类型数组ary中查找元素ele for(int i=0;isize;i+)/遍历数组 if(ele=aryi)/如果找到 return i;/返回元素当前位置return-1;/未找到则返回-1in
20、t main()/实例化函数模板 int i_ary6=1,2,3,4,5,6;/定义含6个元素的整型数组 double d_ary5=1.1,2.2,3.3,4.4,5.5;/定义含5个元素的浮点型数组 cout 整数4的位置:search(i_ary,4)endl;/在i_ary中查找4 cout 整数12的位置:search(i_ary,12)endl;/在i_ary中查找12 cout 浮点数2.2的位置:search(d_ary,2.2)endl;/在d_ary中查找2.2 cout 浮点数9.9的位置:search(d_ary,9.9)y?x:y);上述代码中的第4行定义了一个指向
21、函数的指针变量,其中值得注意的是,int和“(”之间可以存在空格也可以不存在空格。正如取一般函数的地址一样,在取函数模板的地址前也需要先定义一个指向该模板实例的函数指针,以便把所取得的地址赋给该指针变量。该函数指针的类型就是用来实例化函数模板的参数。18http:/ 7.2.3 函数模板实参的推演函数模板不是真正的函数,在使用前必须先实例化。因而,在调用函数模板(实际上是调用实例化得到的函数)的过程中必然存在一个实例化的过程,而函数模板的实例化的先决条件是求得模板实参。1)函数模板实参的推演过程当函数模板被调用时,从函数实参确定模板实参的类型和值的过程叫做模板实参推演。函数模板实参推演大致分两
22、步进行。(1)编译时,编译器依次检查每个函数实参,以确定在每个函数实参的类型中出现的模板参数。(2)如果找到模板参数,则通过检查函数实参的类型,推演出相应的模板实参的类型或值。例如,对于下面的程序:template int search(T arysize,T ele);/声明查找函数模板:在T类型数组ary中查找元素eleint i_ary6=1,2,3,4,5,6;int i=search(i_ary,4);/在数组i_ary中查找419http:/ 程序中给出了一个函数模板search,它的返回值类型为int,接受两个参数,即大小为size、类型为T的数组和类型为T的用以保存要查找的数的
23、变量。第3行定义了一个含6个元素的整形数组,第4行是函数模板search的调用,传入的实参分别是数组i_ary和4。该函数模板调用的实参推演过程如下。(1)检查函数实参i_ary和4,确定它们的类型都为int型。(2)根据函数模板的声明及步骤(1)的查找,通过实参数组i_ary得到第一个模板类型参数T的值是int型,以此类推,通过实参4得到第二个模板类型参数T的值也是int型,同时,数组i_ary的大小是6,所以模板实参size的值是6。需要说明的是:(1)由于在C+语言中,函数调用可以不取返回值,所以在函数模板实参推演的过程中,函数的返回值类型并不参与其中。试想,既然程序中没有返回值信息,那
24、么也无法根据返回值类型来进行实参推演了。(2)函数模板实参推演的过程中,函数的形参和实参的类型不必完全匹配,只要能够将实参转换为形参的类型,即实参与形参之间存在自动的类型隐式转换即可。多个函数实参可以参加同一个模板实参的推演过程。如果模板实参在函数参数表中出现多次,则每个推演出来的类型都必须相同。如果推演的类型不相同,则调用将会出错,20http:/ 例如:template int max(T x,T y)return(xy?x:y);int main()short a;int b;max(a,b);return 0;程序中第10行的调用是错误的,因为调用max时的实参类型不相同,从第一个实参
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+程序设计第7章 函数模板教学课件 C+ 程序设计 函数 模板 教学 课件
限制150内