版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章模板 本章主要内容:6.1函数模板
6.2类模板
6.3类模板与静态成员
6.4*类模板与友元
6.5*继续前行
计算机学院李卫明
C++提供了模板机制,通过模板可以建立具有通用类型的函数库或类库,为一系列逻辑功能相同而数据类型不同的函数或类创建框架。模板可使程序可以处理在编程时未知类型的对象,对象的类型在使用时才确定。模板提供的多态是一种参数化多态,是编译时多态。模板提供了一种重用程序源代码的有效方法,是STL泛型编程基础。模板是对具有相同特性的函数或类的再抽象,它的本质就是将所处理的数据类型说明为参数,这样可使一段程序代码能用于处理多种不同类型的数据,提高了代码的可重用性。
函数抽象成函数模板,类抽象成类模板,函数模板产生实例函数,也称模板函数,类模板产生实例类,也称为模板类。计算机学院李卫明6.1 函数模板
6.1.1 函数模板与模板函数
下列重载函数分别返回两个整型数、两个双精度实型数以及两个字符串对象中大的对象引用:constint&Max(constint&a,constint&b);constdouble&Max(constdouble&a,constdouble&b);conststring&Max(conststring&a,conststring&b);这些函数代码实现了三个处理不同类型的数据最大值函数的重载(函数定义在下页),名字都叫Max,程序代码中实际调用时,编译器可根据实参类型确定调用的某个具体函数,如下面语句段中:
strings1(strCity1),s2(strCity2);cout<<Max(3,5)<<endl;//调用第一个Max函数
cout<<Max(3.8,2.5)<<endl;//调用第二个Max函数
cout<<Max(s1,s2)<<endl;//调用第三个Max函数计算机学院李卫明constint&Max(constint&a,constint&b){if(a>b)returna;elsereturnb;}constdouble&Max(constdouble&a,constdouble&b){if(a>b)returna;elsereturnb;}conststring&Max(conststring&a,conststring&b){if(a>b)returna;elsereturnb;}计算机学院李卫明上述函数重载为程序可读性带来了便利。同时,上述三个Max函数代码的处理逻辑相同,只是所处理数据的类型不同,存在很大程度上的重复,而且,这样定义的三个函数不适合返回未确定类型对象的最大值:如将来我们设计了苹果类,并且定义了以苹果直径比较苹果大小的方法,我们还得为返回大苹果对象定义相应的Max函数。C++为我们提供了函数模板(functiontemplate)机制解决这一需求。具体就是对处理逻辑相同、只是所处理数据类型不同的函数,将其中类型参数化,抽象成函数模板,再由编译器通过函数模板产生模板函数(templatefunction)。正如函数调用前必须有函数声明或函数定义一样,调用模板函数前,必须先有函数模板声明或函数模板定义。函数模板声明的一般格式如下:template<typename类型参数名>返回值类型函数模板名(形参表);或template<typename类型参数名1,typename类型参数名2,…>返回值类型函数模板名(形参表);两个T类型对象间求大的对象的Max函数模板声明如下:template<typenameT>constT&Max(constT&a,constT&b);计算机学院李卫明函数模板定义的一般格式如下:template<typename类型参数名>返回值类型函数模板名(形参表){ ……//函数模板体}或template<typename类型参数名1,typename类型参数名2,…>返回值类型函数模板名(形参表){ ……//函数模板体}计算机学院李卫明Max函数模板定义如下:template<typenameT>constT&Max(constT&a,constT&b){if(a>b)returna;elsereturnb;}函数模板是对函数的抽象,描述的是使用类型参数来产生一组函数的模板,它不是某一个具体的函数。使用函数模板时,编译器可根据需要,将函数模板内的所有类型参数取某一个具体的数据类型后生成具体的函数代码,生成的函数称为模板函数,在程序中真正执行的代码是模板函数的代码。计算机学院李卫明下述两种在表达式中使用函数模板的方式都可让编译器生成并调用该模板函数:函数模板名(实参表)或函数模板名<类型1,类型2,…>(实参表)第一种使用方式,编译器将根据实参类型推断出函数模板中类型形式参数对应的具体类型,第二种方式中,<类型1,类型2,…>称为类型实参表,编译器无需推导,用类型实参表中的类型来确定类型形式参数对应的具体类型。编译器确定类型形式参数对应的具体类型,生成模板函数并调用,这一工作在编译阶段完成。
如果编译器无法根据实参推断出函数模板中类型形式参数对应的具体类型、存在多种推断方案或者类型实参表提供的类型不符合函数模板要求,编译器将报错。计算机学院李卫明下列语句是函数模板Max的使用示例。
charstrCity1[]="BeiJing";charstrCity2[]="ShangHai";strings1(strCity1),s2(strCity2);cout<<Max(3,5)<<endl;//推断T为intcout<<Max(3.8,2.5)<<endl;//推断T为doublecout<<Max(s1,s2)<<endl;//推断T为stringcout<<Max<string>("BeiJing","ShangHai")<<endl;//指定T为stringcout<<Max("HangZhou","ShangHai")<<endl;
下列语句因编译器无法推断出参数类型,将报错://实参类型分别是constchar[8]constchar[9]//错误:cout<<Max("BeiJing","ShangHai")<<endl;//实参类型分别是char[8]char[9]//错误:cout<<Max(strCity1,strCity2)<<endl;计算机学院李卫明函数模板Max中类型参数T必须符合可进行<比较的要求,Max不可用于不能进行<比较的2个对象。如将来我们设计了苹果类,定义了以苹果直径比较苹果大小的方法,我们就可以对2个苹果对象使用Max函数模板,返回大的一个苹果,Max函数模板达到了适用于函数模板设计时还未知新类型的目的。参数类型推断一般根据实参类型直接进行,很少进行类型转换,如果编译器不能推断出类型或存在多种推断时,编译器会报错,一般可根据编译器错误提示查找解决办法。模板函数类似于重载函数,但是同一个函数模板类型形式参数具体化(实例化)后的所有模板函数必须执行相同的代码,而函数重载时在每个函数体中可以执行不同的代码,当遇到执行的代码有所不同时,不能简单地套用函数模板,而应像重载普通函数那样进行重载。与普通函数声明位于头文件,函数定义位于.cpp文件不同,一般编译器要求函数模板的定义也位于头文件中或使用函数模板的源程序文件内。计算机学院李卫明6.1.2 内联函数模板
对于频繁调用的简单函数,使用内联函数可提高程序的执行效率。使用内联函数模板同样可提高程序的执行效率。使用内联函数模板,只需在函数模板声明和函数模板定义前加入inline即可,函数模板使用不变。如:template<typenameT>inlineconstT&Max(constT&a,constT&b);Max函数模板定义如下:template<typenameT>inlineconstT&Max(constT&a,constT&b){if(a>b)returna;elsereturnb;}计算机学院李卫明6.1.3 函数模板重载函数模板也可以重载。如:template<typenameT>constT&Max(constT&a,constT&b);//2个中找出最大template<typenameT>constT&Max(constT&a,constT&b,constT&c);//3个中找出最大template<typenameT>constT&Max(constTA[],intiCount);//iCount个元素数组中找出最大template<typenameT>constT&Max(constTA[],intiCount){intm=0;for(inti=1;i<iCount;++i)if(A[i]>A[m])m=i;returnA[m];}//其它函数模板定义省略计算机学院李卫明程序中遇到如下使用函数模板的代码段时,编译器分别根据函数模板使用时实参类型和个数,从不同的函数模板推断出类型参数的实际类型后生成模板函数代码再进行调用。
intA[]={1,3,5,7,9};cout<<Max(6,5)<<endl;cout<<Max(3,5,8)<<endl;cout<<Max(A,5)<<endl;计算机学院李卫明6.1.4 函数模板和函数constchar*Max(constchar*a,constchar*b){if(strcmp(a,b)>=0)returna;elsereturnb;}//2个中找出最大template<typenameT>constT&Max(constT&a,constT&b){if(a>b)returna;elsereturnb;}计算机学院李卫明C++容许定义同名的函数模板和函数。重载函数模板和函数后,编译器首先匹配类型完全相同的函数,如果匹配失败,再寻求函数模板进行匹配。
重载Max函数模板和Max函数后,下述语句可以输出正确结果。
charstrCity1[]="BeiJing";charstrCity2[]="ShangHai";strings1(strCity1),s2(strCity2);cout<<Max(s1,s2)<<endl;//调用模板函数
cout<<Max<string>("BeiJing","ShangHai")<<endl;//调用模板函数
cout<<Max("HangZhou","ShangHai")<<endl;//调用函数
cout<<Max("BeiJing","ShangHai")<<endl;//调用函数
cout<<Max(strCity1,strCity2)<<endl;//调用函数C++还具有函数模板特化和部分特化机制,在此不再展开。计算机学院李卫明6.2 类模板
6.2.1 类模板与模板类前面学习了如何设计和使用类,例如,我们需要整形栈时,我们设计实现了整形栈类,然后,利用整形栈类建立整形栈对象,解决实际问题。假如程序设计中我们还需要用栈来保存字符串对象,我们需要再设计实现字符串栈类,而整形栈类和字符串栈类的功能相同,实现的方法也相同,只是处理的数据类型不同,这样的代码存在很大程度上的重复,而且,将来如果需要设计实现了苹果类,我们需要在栈中保存苹果时还是需要设计实现苹果栈类。与函数模板类似,C++支持类模板(classtemplate)。上述情形下,我们只需要将栈类中保存的元素类型参数化后,抽象成栈类模板即可,需要时,再由编译器根据栈类模板自动生成具体栈类的代码,栈类就是栈类模板的实例,再通过栈类模板生成的栈类建立栈对象,满足实际需求,根据类模板自动生成的实例类称为模板类(templateclass)。类模板是类的抽象,模板类是类模板绑定模板参数后形成的实例。C++STL正是以类模板形式提供各种容器或容器适配器,大大提高了代码的通用性,对效率丝毫没有影响。我们在第二章样例中使用的正是类模板绑定模板参数后生成的模板类,如整形栈正是STLstack类模板绑定整形参数后编译器生成的整形栈模板类,字符串链表正是STLlist类模板绑定string类型参数后编译器生成的字符串链表模板类。计算机学院李卫明6.2.2 类模板定义和成员函数实现类模板是参数化的类。类模板定义与类的定义类似,只是以关键字template开始,尖括号里含类型参数,一般形式如下:template<typename类型参数名>class类模板名{……//类模板体};或template<typename类型参数名1,typename类型参数名2,…>class类模板名{……//类模板体};类模板声明和实现中,关键字typename可以用关键字class代替,效果相同。参数类型可以用于定义数据成员的类型、函数成员的参数和返回值类型,还可用于定义类模板内的类型成员。计算机学院李卫明与类的成员函数定义相同,类模板的成员函数不但可以在类模板内定义,也可以在类模板外定义。在类模板外定义时,需要采用下面的形式:template<typename类型参数名>返回值类型类模板名<类型参数名>::成员函数名(形参表){ ……//函数体}或template<typename类型参数名1,typename类型参数名2,…>返回值类型类模板名<类型参数名1,类型参数名2,…>::成员函数名(形参表){ ……//函数体}计算机学院李卫明由类模板产生模板类的形式如下:类模板名<类型>或类模板名<类型1,类型2,…>此处实际类型必须由程序员指明,不能由编译器推断。编译器据此生成一个具体的模板类,然后使用模板类建立和使用具体对象。生成一个具体的模板类工作在编译阶段完成。
EX6.1是一个实现了动态数组功能的Array类模板的简单实例。程序中先定义了Array动态数组类模板,实现了类模板中成员函数(成员运算符),在主程序中,建立了可存放n个整数的整形动态数组模板类对象obj,再进行了存取、输出,又建立了可存放n个字符串的字符串动态数组模板类对象strObj,其中每个字符串对象缺省构造为空字符串,再对数组对象进行了存取、输出;程序执行完毕后,2个动态数组对象进行析构,包含其中所有字符串对象的析构,不会造成任何内存泄漏。样例程序中进行了下标合法性判断,如果下标越界,会抛出异常,关于异常,详见下一章。具体代码见Ex6.1。计算机学院李卫明6.2.3 内联成员函数、非类型参数、函数调用运算符重载与类声明(定义)内实现的成员函数是内联成员函数一样,直接在类模板定义里实现的成员函数也是内联成员函数。如果在类模板定义内的成员函数声明前面带有inline关键字说明,这样的成员函数也是内联成员函数。本章后续栈类模板样例中的入栈、出栈等函数声明前面带有inline关键字的成员函数都是内联成员函数。在模板的类型形参表中不但可以包含常规类型参数,还可以包含非类型参数。非类型参数表示一个值,而不是一个类型。我们通过一个类型名而非关键字typename或class指定非类型参数。当模板实例化时,非类型参数被一个常量值所代替,这个常量值可以被模板用户提供或编译器推导出来。本节数组类模板样例Ex6.2中MaxSIZE正是这样的非类型参数,数组Array类模板实例化时由用户提供的值为6的常量n代替,代表模板类的数组容器可以存放6个元素。C++STL应用中经常用到函数对象,作为算法的实参。所谓函数对象实际就是重载了函数调用运算符()的类的实例,这样的对象可以象普通函数一样使用,STL中也称为仿函数。用户自定义对象通过运算符()调用函数,C++规定函数调用运算符()只能重载为类的成员函数,具体声明格式如下:返回值类型operator()(形参表);在C++中,由于上面声明格式中形参表的参数个数可以是不确定的,因此,函数调用运算符()是不确定目数运算符。
具体代码见Ex6.2。计算机学院李卫明6.2.4 类模板名简化表示模板类类模板是生成模板类的蓝图,不是一个类型;类模板绑定模板参数后形成的类模板实例,也就是模板类,才是一个类型。模板类可以出现在程序中需要类型的位置,类模板名则不可以。这一规则有一个例外,就是在本类模板作用域中,模板类可以简化为类模板名,省略类模板的模板参数绑定;在类模板作用域外,模板类不可以简化为类模板名,必须绑定模板参数。
本章后面样例Ex6.3和Ex6.4中CStack类模板,在类模板定义和类模板成员函数定义中,也就是CStack类模板作用域中,需要类型的位置可以用类模板名简化表示,如Ex6.3中语句:
CStack(constCStack&rhs);//拷贝构造函数
CStack&operator=(constCStack&rhs);//赋值运算符重载是下面2个语句的简化表示:
CStack<T>(constCStack<T>&rhs);//拷贝构造函数
CStack<T>&operator=(constCStack<T>&rhs);//赋值运算符重载类模板作用域外,也就是界定符::前不可以用类模板名简化表示模板类。具体参见后面样例代码Ex6.3、Ex6.4。计算机学院李卫明6.2.5 典型范例——链栈类模板设计和实现
Ex6.3在第2章2.6节单链表表示的整形栈基础上,又增加了拷贝控制函数后进一步抽象成链栈类模板,并在此基础上增加了类模板的友元函数Dump,用于将栈内元素倒出并显示,展示了类模板友元函数的用法。语句5~9和语句36是有关类模板的友元函数声明,详细解释在下一节。本样例中采用指令方式测试类模板,Parse函数模板的不同实例负责整形指令和字符串指令的解释。输入描述开始int或string代表需要处理的对象类型。对于每种类型,先构造两个目标类型的空栈S1、S2,读入整数n,再读入n对指令v、x;1<=v<=2;将元素x入第v个栈,n对指令处理完成后,通过拷贝构造和赋值、移动构造和移动赋值4个函数,根据S1拷贝构造S3、S3移动构造S5,无参构造空栈S4、S6,S2复制赋值给S4、S4移动赋值给S6,最后,倒出显示S1、S2、S5、S6四个栈中元素。输出描述每个栈中输出占一行,元素间以空格分隔。具体代码见Ex6.3。计算机学院李卫明6.2.6 内嵌容器的栈类模板设计和实现
Ex6.4设计和实现的栈类模板采用参数化类型的内嵌容器表示栈内元素,利用容器的功能,改造接口后实现了栈类模板。样例中数据成员m_cont的类型是模板的第二参数,缺省是绑定栈元素同样类型的双端队列,实际使用时可以采用缺省类型(语句105),也可以绑定字符串链表模板类。Parse函数模板同样采用了多类型参数和缺省参数的方式实现。采用内嵌容器作为子对象后,本样例中栈类模板内元素存放空间虽然最终采用了动态分配,但类模板本身无需直接动态分配,因而,本例中的构造函数、拷贝控制函数(拷贝构造、移动构造、复制赋值、移动赋值、析构函数)直接由编译器合成即可,大大简化了程序,是现代C++程序设计值得推荐的方式。STL中stack和queue正是利用类似的内嵌容器功能的方法实现的,因而称为容器适配器。具体代码见Ex6.4。计算机学院李卫明6.3 类模板与静态成员类模板也可以同样定义静态数据成员和静态函数成员。类模板的静态数据成员和静态函数成员为相同参数实例化的模板类所共享,不同参数实例化的模板类具有不同的静态数据成员和静态函数成员。下面程序片段中,TSample类模板定义了静态数据成员m_iCount和静态函数成员GetCount,完成了静态数据成员的初始化。template<classT>classTSample{public:staticintGetCount();TSample(){++m_iCount;}private:staticintm_iCount;};计算机学院李卫明template<classT>intTSample<T>::GetCount(){returnm_iCount;}template<classT>intTSample<T>::m_iCount=0;模板类TSample<int>和TSample<string>分别具有不同的静态数据成员和静态函数成员的实现,模板类TSample<int>和TSample<string>分别建立了2个和1个实例,构造函数中利用静态数据成员完成实例计数,添加少量代码形成完整程序后,后面测试语句输出结果分别是2和1。TSample<int>t1,t2;TSample<string>st1;cout<<TSample<int>::GetCount()<<endl;cout<<TSample<string>::GetCount()<<endl;计算机学院李卫明6.4 *类模板与友元正如类可以具有友元函数和友元类一样,类模板也可以具有友元函数和友元类。类模板的友元函数和友元类关系比较复杂,下面分别预以介绍。6.4.1 类模板的普通友元普通函数和普通类作为类模板的友元情况比较简单。普通函数和普通类是每个类模板的实例的友元。下面程序片段中,普通函数SomeFriendFun和普通类FC是每个类模板TSample的实例的友元,形成多个模板类共同拥有一个友元函数和友元类关系。classFC;template<classT>classTSample{public:friendclassFC;friendvoidSomeFriendFun();//省略其余成员};计算机学院李卫明6.4.2 普通类的友元模板函数模板和类模板作为普通类的友元时,每个函数模板的实例-模板函数和每个类模板的实例-模板类都是这个类的友元。下面程序片段中,函数模板TFun的每个实例和类模板TC的每个实例都是CSample类的友元,形成1个类拥有多个友元函数和友元类关系。classCSample{public:template<classT>friendclassTC;template<classT>friendvoidTFun(T);//省略其余成员};计算机学院李卫明6.4.3 多对多关系的类模板和友元模
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 能源工程专业就业薪资
- 高压配电装置的运行维护
- (正式版)DB42∕T 2536-2026 杉木退化矮林近自然经营技术规程
- 2026重庆市涪陵区国有企业第二季度招聘控制数工作人员9人考试模拟试题及答案解析
- 2026内蒙古巴彦淖尔市临河区老年大学班主任储备人才招募考试备考题库及答案解析
- 2026江西赣州上犹县总医院东山医院招募见习生1人考试备考题库及答案解析
- 2026江苏南京大学现代生物研究院特任副研究员招聘1人考试备考题库及答案解析
- 2026年大庆油田总医院招聘消化科医生岗位工作人员2人笔试备考题库及答案解析
- 2026四川成都市公共交通集团有限公司招聘投资管理专员岗位考试模拟试题及答案解析
- 2026四川南充阆中市第二批招募青年就业见习人员67人笔试备考题库及答案解析
- 【物理】2026郑州高三二模试题及答案
- 《AQ3067-2026化工和危险化学品重大生产安全事故隐患判定准则》解读
- 施工电梯基础监理实施细则
- 国家机关事务管理局所属事业单位2025年度公开招聘应届毕业生笔试模拟试题
- 服装压力舒适性的测试与评价体系构建
- 2026年钻探工技师考试题及答案
- 2026年河北邢台市中考数学试题及答案
- 2026年高考地理高分冲刺学习指南
- 第二单元 辽宋夏金元时期:民族关系发展和社会变化 单元总结 - 学生版
- 国开2026年《公共政策概论》形成性考核任务1-4答案
- 2026年3月版-安全环境职业健康法律法规、规章、标准文件清单
评论
0/150
提交评论