下载

2下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 C++课件--薛景瑄chapter_6

C++课件--薛景瑄chapter_6.doc

C++课件--薛景瑄chapter_6

jingernanhang
2018-09-05 0人阅读 举报 0 0 暂无简介

简介:本文档为《C++课件--薛景瑄chapter_6doc》,可适用于IT/计算机领域

第六章:模板函数中加“不同数据类型”例子时间增至–小时第六章模板(template)参数化多态性概念模板机制的设计和细节是由BjarneStroustrup在其年月发表的名为“ParameterizedTypesforC”一文中披露的。现在通过以下例子来看模板的概念和用途。例对象内数组既用于存储浮点型变量又用于存储整型变量使用中不方便。templcppwithreferencetotemplcppandtemplcppastemplateisnotused,youhavetodotypeconversiontwiceforintergersinspiredbychapter"generictype"ofbook"C#程序设计教程"郑宇军编著#include<iostreamh>classarray{intsizedouble*elementpublic:array(ints)~array()doubleread(intindex)voidenter(intindex,doublevalue)}array::array(ints){size=selement=newdoublesize}array::~array(){deleteelement}doublearray::read(intindex){returnelementindex}voidarray::enter(intindex,doublevalue){elementindex=value}voidmain(){arrayobj()doubleddcout<<"Enteradoublevariableplease:"cin>>ddobjenter(,dd)cout<<objread()<<endlarrayobj()intvcout<<"Enteranintegerplease:"cin>>vtypeconversionneededtwiceobjenter(,(double)v)如用objenter(,v)则整型变量例如存入后将会被误认为零。cout<<(int)objread()<<endl如用cout<<objread()<<endl则输入例如Enteranintegerplease:后将错误地显示e}*Results:Enteradoublevariableplease:Enteranintegerplease:*上例中classarray的数组成员double*element是一个指针。在建立第一个对象obj时使用obj内的指针objelement在堆区内分配一个供浮点型数组用的空间。在建立第二个对象obj时使用obj内的指针objelement也在堆区内另外分配一个供浮点型数组用的空间。由于浮点型变量的长度大于整型变量所以此空间也可用于存储整型变量。但在存储和显示整型变量时必须进行显式类型转换。具体而言如只用语句“objenter(,v)”而不用“objenter(,(double)v)”则整型变量例如存入后将会被误认为浮点型零值。同样地如只用语句“cout<<objread()<<endl”则虽不出现编译错误但整型变量将被显示为e程序依然出错。因此必须使用如下语句:cout<<(int)objread()<<endl总之使用浮点型数组存储整型变量时在输入变量和显示变量时必须两次使用类型转换。为避免此麻烦可依靠模板(template)。见以下程序:templcppwithreferencetotemplcppandtemplcpptrytoillustratetheadvantageoftemplatesastemplateisused,typeconversionisnolongernecessaryinspiredbychapter"generictype"ofbook"C#程序设计教程"郑宇军编著#include<iostreamh>template<typenameT>也可用template<classT>classarray{intsizeT*elementpublic:array(ints)~array()Tread(intindex)voidenter(intindex,Tvalue)}template<typenameT>array<T>::array(ints){size=selement=newTsize}template<typenameT>array<T>::~array(){deleteelement}template<typenameT>Tarray<T>::read(intindex){returnelementindex}template<typenameT>voidarray<T>::enter(intindex,Tvalue){elementindex=value}voidmain(){array<double>obj()doubleddcout<<"Enteradoublevariableplease:"cin>>ddobjenter(,dd)cout<<objread()<<endlarray<int>obj()intvcout<<"Enteranintegerplease:"cin>>vNomoretypeconversionneededobjenter(,v)cout<<objread()<<endl}*Results:Enteradoublevariableplease:Enteranintegerplease:*以上程序中使用模板参数T表示变量类型。在建立对象时用int或double替代T即可!在建立第一个对象obj时语句为“array<double>obj()”其中<double>表示使用double替代T以便在堆区内分配一个供浮点型数组用的的空间。而在建立第二个对象obj时语句为“array<int>obj()”其中<int>表示使用int替代T以便在堆区内分配一个供整型数组用的的空间。随后在输入和显示整型变量时就不必进行类型转换。这种做法称为参数化(parameterization)。是将数据类型作为参数进行传递该形参T的实参可以是预定义数据类型或用户自定义数据类型(主要是类的对象)这是参数化的多态性。实现参数化多态性的主要工具是模板(template)。传递参数的过程中T称为模板形参(templateparameter)或类型形参(typeparameter)int和double称为模板实参(templateargument)或类型实参(typeargument)。由于C语言的程序结构主要是由函数和类构成的因此模板也具有两种不同的形式:函数模板和类模板。实例化实例化实例化函数模板函数模板与模板函数其实早在C语言中就有解决参数化的简单方法。可以使用#define预处理语句解决不同形参类型的问题例如:使用#defineTint则Tmax(Tx,Ty){return(x>y)x:y}可用于处理整型变量。而使用#defineTdouble则Tmax(Tx,Ty){return(x>y)x:y}可用于处理double型变量。但如需同时处理两种以上的变量就须要定义两种以上类型不太方便。在C中第五章中所讲函数重载的方法能够处理较为复杂的情况并且调用方便。但也有一个不便之处:在调用重载函数(overloadedfunction)之前必须事先将所有可能碰到的函数形参都加以定义。不然就无法调用。为克服这个不便之处可使用函数模板。它比函数重载更方便更灵活。以§“普通函数的重载”例平方函数的重载中的程序overldfuncpp为例。可使用函数模板如下:TemplfuncppTemplateofgettingsquare#include<iostreamh>template<typenameT>Tsq(Ta){returna*a}voidmain(){inti=longl=用户可按需要任意调用cout<<sq(i)<<endlcout<<sq(l)<<endlcout<<sq()<<endl}*Results:*以上程序中定义了函数模板然后在主函数中建立了三个模板函数其中sq(i)使用int类型将类型形参T实例化sq(l)使用long类型将类型形参T实例化最后sq()使用double类型()将类型形参T实例化。可见在函数模板中使用类型参数T(或其它更多类型参数例如X、Y、Z等等)作为形参这不是具体函数而是函数的抽象。等到进行实例化即代入具体类型实参例如int、double等以后就建立了具体函数被称为模板函数。实例化实例化实例化根据以上图形可知:在使用函数模板时首先函数模板中参数类型被预定义数据类型或用户自定义数据类型所替代也即函数模板被实例化(instantiated)为模板函数然后该模板函数再被调用。函数模板的使用例带形参的函数模板:用于求变量的绝对值以及求两个变量中的最大值TemplfuncppTemplatesofdeterminingabsolutevaluesandmaxvalues#include<iostreamh>template<typenameT>Tabs(Ti){return(i<)i:i}template<typenameT>Tmax(Ta,Tb){return(a>b)a:b}voidmain(){inti=,j=,k=doubled=,e=,f=cout<<"abs(k)="<<abs(k)<<endlcout<<"abs(f)="<<abs(f)<<endlcout<<"max(i,j)is"<<max(i,j)<<endlcout<<"max(d,e)is"<<max(d,e)<<endl}*Results:abs(k)=abs(f)=max(i,j)ismax(d,e)is*例函数模板的重载:函数模板中求数组各元素之和templovrcppcopiedfromMai'sbook,pwithmistakescorrected,notforindividualcharacters#include<iostreamh>template<typenameT>Tsum(Tarray,intsize){Ttotal=for(inti=i<sizei)total=arrayireturntotal}template<typenameT>Tsum(Tarray,Tarray,intsize)重载的函数模板{Ttotal=for(inti=i<sizei)total=arrayiarrayireturntotal}voidmain(){intintarr={,,,,}intintarr={,,,,}doubledouarr={,,,,}cout<<"Sumofelementsofoneintegerarrayis"<<sum(intarr,sizeof(intarr)sizeof(intarr))<<endlcout<<"Sumofelementsofonedoublearrayis"<<sum(douarr,sizeof(douarr)sizeof(douarr))<<endlcout<<"Sumofelementsoftwointegerarraysis"<<sum(intarr,intarr,sizeof(intarr)sizeof(intarr))<<endl}*Results:SumofelementsofoneintegerarrayisSumofelementsofonedoublearrayisSumofelementsoftwointegerarraysis*在混合使用重载的函数和模板时应当避免二义性。此时它们的调用顺序是:先调用匹配的重载函数。如无匹配的重载函数则调用匹配的函数模板。如果找到则将其实例化建立一个模板函数。如再匹配不上则调用相近的重载函数此时可能丢失精度。因此可粗略地归纳为:先重载再模板后相近。例混合使用重载的函数和模板templovrcppoverloadedfunctionsandtemplatesmixed#include<iostreamh>template<typenameT>voidmax(Ta,Tb){Tx=(a>b)a:bcout<<"template:"<<x<<endl}voidmax(inta,doubleb){doublex=(a>b)a:bcout<<"overloadedint,double:"<<x<<endl}voidmax(doubleb,inta){doublex=(a>b)a:bcout<<"overloadeddouble,int:"<<x<<endl}voidmain(){inti=,j=doubled=,f=charch='A',hc='C'max(i,j)max(d,f)max(ch,hc)max(d,j)max(i,d)max(ch,f)}*Results:template:template:template:Coverloadeddouble,int:overloadedint,double:overloadedint,double:*重载函数与函数模板都属于多态性机制它们的区别如下:()主函数调用重载函数和模板函数的格式完全相同。重载函数的优点是能够对不同数据类型使用不同程序逻辑进行类似操作。例如用于求取int或double的最大值的程序逻辑和操作就和求取char或char*的最大值的程序逻辑和操作差别很大。而函数模板无法处理。如果每种数据类型的程序逻辑和操作相同则使用函数模板将会更加简洁和方便。模板是一种代码产生机制。()重载函数中无论它们使用与否全部都存于代码区中都占用空间。而在函数模板中只当使用一个具体参数类型时才建立一个模板函数而当使用另一个具体参数类型时再建立另一个模板函数。编程方便使用灵活效率较高。类模板类模板与模板类现在看类模板与模板类的关系。例具有一个类型参数的类模板templclscppclasstemplatewithoneparameterargument#include<iostreamh>template<typenameT>classA{public:voidf(Ta){cout<<a<<endl}}voidmain(){A<char>bbbbf('A')A<double>ccccf()}*Results:A*例具用多个模板参数的类模板templclscppclasstemplate#include<iostreamh>template<classX,typenameY>classA{public:voidf(Xa){cout<<a<<endl}voidg(Yb){cout<<b<<endl}}voidmain(){A<char,int>bbbbg()bbf('A')A<double,int>ccccg()ccf()}*Results:A*例类模板中使用省缺(default)参数第一例templclscppdefaultparameterisused#include<iostreamh>template<classX,typenameY=X>'X'isadefaultparameterclassA{public:voidf(Xa){cout<<a<<endl}voidg(Yb){cout<<b<<endl}}voidmain(){A<char>bbequivalentto<char,char>bbg('Z')bbf('A')A<int,double>ccccg()ccf()}*Results:ZA*例类模板中使用省缺参数第二例templclscppdefaultparameterisused#include<iostreamh>template<typenameX,typenameY=int>'int'isadefaultparameterequivalenttofun(intj=){……}classA{public:voidf(Xa){cout<<a<<endl}voidg(Yb){cout<<b<<endl}}voidmain(){A<char>objieA<char,int>objg()objf('A')A<int,double>objobjg()objf()}*Results:A*例类模板中也可混合地使用非类型参数templclscppNontypeparameterisusedOnlyconstdatacanbeusedasrealargumetsoftemplates#include<iostreamh>template<classT,intn>'n'isanontypeparameterclassA{public:voidf(Ta){cout<<a<<endl}voidg(){cout<<n<<""}}voidmain(){A<char,>objaobjag()objaf('A')A<double,>objbobjbg()objbf()intfive=error:'A':invalidtemplateargument'n'constintFIVE=A<double,FIVE>objcobjcg()objcf()}*Results:A*以上程序中除数字数据(例如)外常量数据(例如FIVE)也可用作模板的实际参数(realparameter)但一般变量不能使用。实例化实例化实例化实例化实例化实例化根据以上图形可以知道:在使用类模板时首先类模板中参数类型被预定义数据类型或用户自定义数据类型所替代也即类模板被实例化(instantiated)为模板类然后该模板类再一次被实例化为类的对象最后该类的对象可被调用。类模板有时也称为参数化数据类型。类模板用作函数形参例类模板用作独立函数display()的形参。看一个类似§中templcpp的程序。此程序中使用两个文件:头文件和应用文件第一个是头文件:arrayhcopiedfromZhangGuoFeng'sbook,#if!defined(ARRAYH)#defineARRAYHtemplate<typenameT>classarray{intsizeT*elementpublic:array(ints)~array()Tread(intindex)voidenter(intindex,Tvalue)}template<typenameT>array<T>::array(ints){size=selement=newTsize}template<typenameT>array<T>::~array(){deleteelement}template<typenameT>Tarray<T>::read(intindex){returnelementindex}template<typenameT>voidarray<T>::enter(intindex,Tvalue){elementindex=value}#endifARRAYH第二个是应用文件:tempparcppmodifiedversionofZhangGuoFeng'sbook,p#include<iostreamh>#include"arrayh"template<typenameT>voiddisplay(array<T>x,intindex){cout<<xread(index)<<endl}voidmain(){array<int>a()array<int>b()intvcout<<"Enteranintegerplease:"cin>>vaenter(,v)display(a,)cout<<"Enteranintegerplease:"cin>>vbenter(,v)display(b,)}*Results:Enteranintegerplease:Enteranintegerplease:*其中和都是操作员所键入的。主程序第一句“array<int>a()”产生如下的对象a的双区存储内容:对象a(栈区)堆区第六句aenter(,v)主程序第二句“array<int>b()”产生如下的对象b的双区存储内容:对象b(栈区)堆区第十句benter(,v)类模板的派生以下例子中在类模板的派生中使用不同数据类型。例基类的类模板派生出派生类的类模板时其构造函数的初始化列表要求一定格式。templdercpp#include<iostreamh>template<typenameT>classA{public:A(Ta){cout<<"CONSA:"<<a<<endl}}template<typenameS,typenameT>classB:publicA<T>{public:B(Sa,Tb):A<T>(b){cout<<"CONSB:"<<a<<endl}}template<typenameX,typenameY,typenameZ>classD:publicB<X,Y>{public:D(Xa,Yb,Zc):B<X,Y>(a,b){cout<<"CONSD:"<<c<<endl}}voidmain(){D<int,double,int>obj(,,)D<int,char*,double>obj(,"good",)}*Results:CONSA:CONSB:CONSD:CONSA:goodCONSB:CONSD:*以上程序中各模板派生类构造函数中的初始化列表和我们以前看到的不用模板时的派生类构造函数中的初始化列表不同其中还列出类型参数例如D(Xa,Yb,Zc):B<X,Y>(a,b){cout<<"CONSD:"<<c<<endl}中的B<X,Y>这是类型参数列表它必须和这个类的类首部classD:publicB<X,Y>中的类型参数列表<X,Y>相匹配。这是模板派生类构造函数中的初始化列表的特点。这表示此派生类向上传递的两个参数的类型分别为X和Y也即a的类型为Xb的类型为Y。而这必须和构造函数的参数表匹配即和D(Xa,Yb,Zc)中的“Xa”和“Yb”匹配。此外和不用模板时的派生类构造函数中的初始化列表相同此类型参数列表也应和其直接基类构造函数的参数表匹配即和B(Sa,Tb):A<T>(b){cout<<"CONSB:"<<a<<endl}中的(Sa,Tb)相匹配。例基类和派生类分别使用不同数据类型templdercpp#include<iostreamh>template<typenameT>classA{public:voidf(Ta){cout<<a<<endl}}template<typenameX,typenameY>classB:publicA<Y>{public:voidg(Xb){cout<<b<<endl}}voidmain(){B<char,int>bbbbf()bbg('A')}*Results:A*如果使用以下主程序则运行结果为:voidmain(){B<int,int>bbbbf()bbg()}*Results:*例类模板的多继承templdercppmultiinheritanceofclasstemplate#include<iostreamh>template<typenameT>classA{public:voidf(Ta){cout<<a<<endl}}template<typenameT>classB{public:voidg(Tb){cout<<b<<endl}}template<typenameX,typenameY,typenameZ>classD:publicA<Y>,publicB<Z>{public:voidh(Xc){cout<<c<<endl}}voidmain(){D<char*,int,double>ddddf()ddg()ddh("Yousucceeded!")}*Results:Yousucceeded!*模板参数的传递A<typenameY>B<typenameZ>intdoubleD<typenameX>char*<typenameX,typenameY,typenameZ>char*intdouble其中是模板参数类型的传递路线可以看出模板的类型参数(简称模板参数)的传递和构造函数的数据参数(简称构造函数参数)的传递的格式是不同的具体如下:()在传递构造函数的数据参数时完全通过构造函数的初始化列表。例如:第四章§例中的Z::Z(inta,intb,intc,intd,inte,intf,intg):W(b,c,d,e,f,g),V(f,g){z=a}其中a、b、c、d、e、f和g是变量(数据)的具体数值。()在传递模板的类型参数时在派生类的类定义首部使用类似如下格式:template<typenameX,typenameY,typenameZ>classD:publicA<Y>,publicB<Z>其中X、Y和Z是类型参数。而构造函数的初始化列表则具有如下格式:D(Xa,Yb,Zc):B<Y,Z>(b,c){cout<<"CONSD:"<<a<<endl}(第六章完)size=elementbelement………belement=…belementsize=elementelement…element=……aelementobjcobjbobja模板类A<char>模板类A<double,int>类模板template<classX,typenameY=X>classA{……}模板函数sq(l)模板类A<char,int>模板函数sq(i)模板函数sq()对象函数模板类模板模板模板函数模板类函数模板

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/22

C++课件--薛景瑄chapter_6

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利