第三十四讲 模板函数

       前面三讲的class,有朋友反应有点难了,一时理解不过来,不过没关系,接下来的内容我会让大家弄明白C++的class到底是个什么东西,其实他也不难,虽然他是所有面向对象编程里面最难理解的。
还记得我们在将C语言的时候说到一个简单的计算幂次方的函数吗?当时我们说过这个函数功能过于简陋,只能实现简单的int类型,现在我们来回顾一下当时我们实现的方法:
——————————————–
int  power(int n,int m)
{
     
int sum = 1;


      if(m == 0)
            

return 1;


      while(m–)
            sum *= n;


      

return sum;


}
—————————————
这个函数的极限性事只能实现底数为整形的,那么如果为double的话是不是还得重新实现一个n类型为double的呢?像下面一样:
———————————–
double  power(double n,int m)
{
     double sum = 1.0;


      if(m == 0)
            

return 1;


      while(m–)
            sum *= n;


      

return sum;


}
———————————–
对了,说到这里,好像一直忘了给大家说关于各类型和零值的比较。就比如,如果比较对象是int,那么和零值比较可以这么写:
———————————-
if(int&

== 0) || if(int&

!= 0)
———————————–
如果是指针,则可以这么写:
—————————————————
if(point&

== NULL) || if(point&

!= NULL)
—————————————————–
那么double(或者float)类型的值和0值比较该怎么比较呢?如下吗?
——————————————–
if(double&

== 0) || if(double !=0 )
——————————————–
这是错误的写法,为什么呢?虽然平时我们都认为一个double可以是0,其实不然,那是我们的一种思维习惯,因为doublefloat是有精度的,所以要让他们和0做比较,必须得取一个近似值:
———————————–
double(float) M = 0.00001;


if(double(float)>=-M &

&

double(float) <= M);


————————————-
以上比较才是正常的比较方式,通常情况是double或者float不能使用 == 或者!=来判断,因为不管怎么样,double或者float是不存在等或不等的。
由于bool可以用int来表示,所以关于bool对象和0比较和int的很相似。
————————–
bool flag;
if(flag) || if(!flag);


—————————
据说以上对比是几乎每个C/C++程序员面试都会被问到的说。
好了,现在言归正传,我们重新回到我们的power上来,如果我们每次要用一个类型还都得写个power函数出来的话这岂不是太不效率了吗?所以,为了解决这个问题,我们今天就来看看模板,为了很好的让大家了解什么事模板,我们以一个模板函数来进入主题:
——————————————-
template<typename T>T power(T n,int m)
{
if(m == 0)

return 1;

T sum = 1;

if(m>0)
{
while(m–)
sum *= n;

}
else
{
while(m++)
sum *= n;

sum = 1.0/sum;

}

return sum;

}
—————————————-
然后我们分别用三种类型测试一下这个模板函数:
第一个底数为整形,第二底数为浮点型,指数为正,第三个底数为浮点型,指数为负,分别看看他们的输出情况:
——————————
power(4,3);


power(4.5,3);


power(4.0,-1);


—————————
第三十四讲 模板函数
大家一定注意到了,我把这个模板函数放在我自己的一个名称空间中,所以我们要使用的时候记得添加My_Code::限制域,否则,就算#include “Date.h”也是不可访问的,除非我们再使用using namespace My_code;

这样做虽然方便,但却又有个不便之处,就像当初我们所说的一样,如果我们在某处定义了一个名叫power的函数,而我们名称空间中同时又有了这个power的函数原型,一但我们都是用了using namespace XX的话,当我们使用power的时候编译器就要发疯了,如果参数上面有差别,那还好,编译器会根据参数类型推演出我们想要的原型,如果同时参数类型也相同,那就懵了,所以,不适用using namespace XX虽然调用名称空间的函数不方便,但是至少不会让编译器让以后阅读你代码的人疯掉。
好了,现在重新回来看看我们上面的模板函数:
template<typename T>
template是声明一个模板的关键,他同时是C++的关键词,所以,想要定义一个模板的话,不论是函数还是类,这个必不可少,而且是唯一。
在接下来的一对尖括号<>,也是必不可少,如果说template定义了模板,那么<>里面却定义了类型,我们想要什么类型都必须在这里面声明,就好比:typename T,这句表示声明一个类型T,这个T会根据我们实际使用的时候推演出来,就拿我们的例子来说,当我们底数为整形的时候,这个T就是int或者long什么关于整形的类型,如果我们输入的底数是double或者float时候,T就会成为相应的类型。当然要实现这个类型的关键词,typename不是唯一的,我们还可以使用class,如下:
template<class T>
似乎更多的人更愿意使用class,好像因为class比较好些简单一些吧,不过我的意见是用typename比较好些,因为这可以更好的和类区分开来。
template <typename T,typename U,typename ……>
我们可以根据自己的需要声明多个参数,但是记住,每个参数类型的名字不能相同,就比如下面的方式是错误的:
template <typename T,typename T>
好了,最后,我们再用两个例子来结束模板函数,这两个函数以后讲STL的时候或许还会用到:
——————————————-
//从一个数组里面找出最小的元素:
template<typename T,int size>T min(T (&

r_array)[size])
 {
T min_val = r_array[0];

for(int i=0;

i<size;

i++)
if(r_array[i]<min_val)
min_val = r_array[i];

return min_val;

}
 
//从一个数组中找出最大的元素:
 template<typename T,int size>T max(T (&

r_array)[size])
{
T max_val = r_array[0];

for(int i=0;

i<size;

i++)
if(r_array[i]>max_val)
max_val = r_array[i];

return max_val;

}
—————————————-
第三十四讲 模板函数
 

关于模板函数,今天就说到这里,等到我们说模板类时候还会详细讨论,现在相信大家对模板也有一些了解了,大家不妨试着让喜欢的一些小函数模板化,放在自己的名称空间,创建一个属于自己的类库,等以后实际开发的时候可以拉出用,这是很方便的事

发表评论