C++函数模板的使用

 

函数模板

编程写程序时,经常遇到这样的情况:若干程序单元(如函数定义或者类定义等)中除了所处理的数据类型不同,程序的代码是一样的。

重载和模板的区别

1.使用重载优点是不需要重复给函数取名,调用函数的代码比较整洁,但是函数重载的本质依然是一种人工复制代码段的方法,并没有降低书写程序和修改程序的工作量,如果需要修改函数功能时,必须对所有重名的重载函数中完全相同的算法进行完全相同的修改。

预编译处理技术中的宏定义,可以一定程度上解决代码复制的问题,例如如下

#include <iostream.h>
#define max(x,y) ((x)>(y)? (X):(Y))
void main ()
{
    int    m1 =max(100,200);
    double m2 =max(7.8,5.1); 
}

由于预处理技术只对文本进行处理,不考虑数据类型,因此一段宏定义代码可以对不同的数据类型进行处理。例如程序中的D、E、F 三行可进行正确的宏替换处理。

     但是宏也存在着自身不可克服的缺点,由于预处理机制不考虑宏的数据类型,因此在编译程序时,C++编译器中先进行的数据类型监测机制被避开,不能检查到具体的代码错误位置,如下是错误示范。

#include <iostream.h>
#define max(x,y) ((x)>(y)? (X):(Y))
void main()
{
    float a,b;
    char  c;
    cin>>a>>c;
    b=max(a,c);
     

}

如上,由于宏的参数没有数据类型,在预处理进行替换时,编译器不检查数据类型,结果造成 ,float 和 char 进行比较

C++ 中提供的模板可以解决上述问题,而又不存在在使用宏定义解决代码复制时产生的缺点。模板把功能代码段的“算法”和算法中“计算数据的的数据类型”区分开来,编程者可以先写算法的程序代码,而在定义算法时不确定实际计算数据的数据类型,然后在使用算法时在根据实际情况来确定使用何种数据类型进行计算。

模板有两种,一种是函数模板(function template) ,另一种是类模板(class function) 函数模板是对函数的抽象,类是对象的抽象与描述,而类模板是类的抽象。下面分别进行介绍

函数模板的定义

利用关键字template按照特定格式来书写函数,可以在函数中将数据类型也作为参数,这种特殊的函数写法就称为函数模板,函数模板代表了一组函数,能够依据在程序运行过程中使用情况,来动态地决定这些数据类型当时真正的数据类型,而其他返回值的数据类型,也同样可以利用模板的方式

template <<模板形参表>>

<函数定义>

函数模板的定义以关键字template

函数模板的定义以关键字template开始,接着在符号<和>之间给出该函数的模板形式参数。<模板形式参数表>中可以包含一个或多个<模板参数>,如果有多个,则必须用逗号隔开,模板形参可以有三种形式:

typename<参数名>

class <参数名>

<数据类型><参数名>

第一种,和第二种形式中的关键字typename和calss完全等价,在旧版的C++中仅支持关键字Class 新版本的C++中两种关键字都支持,而且建议尽量使用关键字 typename 第三种形参形式中<数据类型>可以是C++数据基本类型或构造数据类型,<参数名>可以是任意的合法的标识符。

用typename或class关键字的参数称为虚拟类型参数,在实际调用函数时会发现被自动替换确定的数据类型。用<数据类型>定义的参数称为常规参数,具有确定的数据类型,不需要替换,档函数模板只有一个虚拟类型参数时通常写成T。

2.函数模板使用一般的格式

   在实际使用函数模板时将虚拟类型参数替换为具体的数据类型,这种确定了数据类型的函数,称为模板函数,C++编译器通过传递给函数模板的不同数据类型生成一组模板函数,模板函数的调用格式如下:

<模板函数名>[<<模板实参>>](函数实参表)

在调用模板函数的时候,C++编译器需要足够的数据类型信息,以便确定虚拟类型参数应该被替换成哪一种数据类型,数据类型信息确定流程是:

首先编译器扫描<模板实参表>根据模板实参表中列出的数据类型顺序来替换对应的虚拟类型参数,在没有<模板实参表>的情况下,编译器还要扫描<函数实参表>就已经可以让编译器进行正确的数据类型转换,则<模板实参>可以省略,大多数情况下<模板实参表>都不需要书写。例如15.3说明了一般函数模板的写法。

#include<iostream.h>
template <typeanme T>
T max (T var1 ,Tvar2)
{
  return var1 >var2 ? var1"var2;
}

void main ()
{
   int m1= max(100,200);
   double m2= max(7.8,5.1);
   char m3 =max('T','M');
   cout<<m1<<endl;

}

上例 就是是使用了函数模板技术来实现对各种不同的数据类型的数据做完全相同的处理,其中T是一个虚拟类型参数,它既可以作为函数的形参类型,又可以作为函数的返回值类型。

当编译器编译到A程序时,会将实参表中的数据类型传递给函数模板,根据当时的数据类型由函数模板生成一个重载函数即模板函数,这时由于实参类型int,函数中所有的虚拟类型参数T都会被替换。

 

下面将说明函数模板的特殊用法,在无法仅靠函数实参的类型确定虚拟类型参数替换规则的时候,必须在调用模板函数时给出具体的<模板实参表>

使用15.4使用函数模板进行数据类型转换。

#include <iostream.h>
template <typename T ,typename T1>
T cast (T1 x)
{
    return x;
}

void main ()
{
    int a =10 ;
    float b;
    b= cast<float,int>(a);
    cout <<b<<endl;
}

在上例中函数模板cast中有两个虚拟类型参数T和T1。函数cast的功能是把数据类型T1的数据转换成数据表类型T的数据。在程序的A行如果写成: b=cast(a);

则<函数实参表>中只有一个实参,编译器无法确定变量a 的int 型 应该替换给哪一个虚拟类型参数,编译器报错,而A行给出了<模板<模板实参表>,指明第一个数据类型为float ,第二个数据类型为int ,编译器优先根据模板实参表中的数据类型进行虚拟类型参数的替换,避免了错误。

函数模板实现技术根据实际数据类型自动生成一系列具有相同算法而数据类型不同的函数,从而解决了手工复制并修改具有相同算法而仅仅数据类型不同的函数编程方式,提高了程序编程和维护的效率。函数重载技术实现的优点是,可以使得被重载的各个函数的函数体实现不同的算法,而模板函数技术则不可以,因此在实现同名函数处理的时候,要根据具体情况选择不同的实现技术。

关于函数模板的几点说明:

1.在定义<函数形参表>时,每一个数据类型参数的前面都必须书写一个关键字。以下的定义格式是错误的:

    template <typename T1 ,t2>

    T1 func<T1 a, T2 b>{}

在虚拟类型参数T2的前面遗漏了关键字typename 这样的程序时无法通过编译的。

由于上上例 中定义的函数模板max时,所设置的两个参数的类型 是一样的,因此当调用max生成模板函数时,若给予模板的两个实际参数的类型不一样,将发生错误。例如:

    int var =max (7.8,30);

此时编译器,遇到第一个实参时会将虚拟类型参数T解释成double但是当它发现第二次出现T的地方不能再将其解释成double 类型时便发生错误。当模板函数中的某个数据类型参数一旦被确认后,便不可以在改变,这时,可以使用强制类型转换运算符来改变不符合已经被确认的数据类型的变量,例如,以下的语句就了以避免报错

    double val =max (7.8,(double)30);

通过以上说明可以看出,当使用模板技术时,C++编译器使用了数据类型检测机制,可以在编译时预编译先防止程序的书写错误:

3,在调用模板函数时,<模板实参表>在大多数情况下可以缺省,但在某些情况下不可以缺省。例如:

函数模板有多个虚拟类型参数,而在生成模板函数时函数实际参数不足以让编译器确认每一个虚拟类型参数,此时必须明确给出<模板实参表>,

虚拟类型参数没有出现在模板函数的形参表中,编译器无法根据函数实参与形参的对应关系来确定虚拟类型参数要替换成哪一种数据类型,此时必须确定给出<模板实参表>。例将说明这一情况。

#include <iostream.h>
template <typename T>
T average (float a[] ,int n)
{
    float ave =0;
    for(int i =0 ;i<n;i++)
    ave+=a[i];
    return ave/n;
}

void main ()
{
    float score[]={67,89,90,67,56,78.8,467,89};
    int aver;
    aver=average<int>(Score,10);

}

程序A行生成模板函数时,给出模板实参int ,将最后的平均成绩转换为整数。如果不给出<模板实参表>,编译器将无法确定虚拟类型参数T的数据类型。

当<模板形参表>出现常规函数时,在调用函数模板时,必须给出对应的实参

#include<iostream.h>
template <typename T,int K>

T fun (T Var1 ,T Var2)
{
    return (Var1+Var2)/k;
}
void main()
{
    float a,b;
    cin >>a>>b;
    float result =fun <float ,5>(a,b);
    cout << "计算结果:" << result <<endl;
}

在上例中<模板形参>中的K是常规参数,数据类型为int ,在函数中可以直接使用K。由于K是模板形参,只能通过<模板实参表>产地实参值给它。A行给出了模板实参表,为了与<模板形参表>一一对应,首先写float 数据类型与虚拟类型参数T对应,然后数据5就是传给K的具体实参数值。由于编译器优先根据<模板实参表>中的数据类型进行虚拟类型参数的替换,所以虚拟类型参数T被替换为float ,而常规参数K也被确定为5,程序就可以正确运行了。

另外,C++规定,函数模板的模板形参表中常规参数不能有缺省值,如在上,若将函数模板的第一行定义为:

template <typename T ,int K =3>

是非法的。

5)在定义和使用函数模板时,特别容易造成指针的使用错误,下面通过来说明在函数模板中指针的正确使用方法。

如下将定义一个用来求数组中所有元素的最大值的函数Max(),由于数值数组可能是整数数组或浮点数组等,而求数据中所有元素的最大值的算法是一样的,因此可以采用函数模板技术。

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值