函数模板

对于变量的值的交换我们可以采用函数或者宏定义的方法,它们之间有什么不同呢?下面的代码采用函数的方法

#include <iostream>
#include <string>
using namespace std;
void Swap(double& a,double& b)
{
  double c = a;
         a = b;
         b = c;
}
void Swap(int& a,int& b)
{
    int  c = a;
         a = b;
         b = c;
}
void Swap(string& a,string& b)
{
     string c = a;
         a = b;
         b = c;
}
int main()
{
   int a = 0;
   int b = 1;
   Swap(a,b);
   cout<<"a="<<a<<","<<"b="<<b<<endl;
   double c = 2;
   double d = 3;
   Swap(c,d);
   cout<<"c="<<c<<","<<"d="<<d<<endl;
   
   string e = "hhh";
   string f = "aaa";
   Swap(e,f);
   cout<<"e="<<e<<","<<"f="<<f<<endl;
   return 0;
}

结果:

sice@sice:~$ ./a.out 
a=1,b=0
c=3,d=2
e=aaa,f=hhh

可以看出我们的交换逻辑是一样的只能交换的数据格式不同罢了,这样每次换一种数据类型就要新的函数特别麻烦占内存,我们也可以采用宏的形式,如下,但是也有它的缺点

#define SWAP(t, a, b)    \
do                       \
{                        \
    t c = a;             \
    a = b;               \
    b = c;               \
}while(0)

在这里插入图片描述
既然函数和宏定义都有它的缺陷,我们就要引入我们的新知识点——泛型编程,所谓泛型编程就是不考虑具体数据类型的编程方式,改进如下,Swap泛型写法中的T不是一个具体的数据类型,而是泛指任意的数据类型

void Swap(T& a,T& b)
{
   T t = a;
     a = b;
     b = t;
}

函数模板的定义

template关键字用于声明开始进行泛型编程,告诉编译器开始泛型编程
typename关键字用于声明泛指类型,告诉编译器T是一个泛指类型
在这里插入图片描述

函数模板的使用

int类型的调用属于自动推导,编译器传入参数时会知道使用int类型数据
float类型的调用属于显示调用,用于告诉编译器我的数据类型是float的
在这里插入图片描述
改进上述的程序,结果一致

#include <iostream>
#include <string>
using namespace std;
template  < typename T >
void Swap(T& a,T& b)
{
      T  c = a;
         a = b;
         b = c;
}
int main()
{
   int a = 0;
   int b = 1;
   Swap(a,b);  //Swap<int>(a,b)
   cout<<"a="<<a<<","<<"b="<<b<<endl;
   double c = 2;
   double d = 3;
   Swap(c,d); //Swap<double>(a,b)
   cout<<"c="<<c<<","<<"d="<<d<<endl;
   
   string e = "hhh";
   string f = "aaa";
   Swap(e,f);  //Swap<string>(a,b),将T转化为string再调用
   cout<<"e="<<e<<","<<"f="<<f<<endl;
   return 0;
}

**函数模板能大程度的减少代码的复用率,它能够根据实参对参数类型进行推导,是C++中重要的代码复用方式,**如下代码对数组的内容进行排序!!!
代码:

#include <string>
#include <iostream>
using namespace std;
template <typename T>
void Sort(T a[],int len)
{
  for(int i=0; i<len; i++)
  {
    for(int j=i; j<len; j++)
    {
        if( a[i] > a[j] )
        {
            swap(a[i], a[j]);
        }
    }
  }

}
template <typename T>
void swap(T&a,T&b)
{
   T c = a;
     a = b;
     b = c;
}
template <typename T>
void printfn(T a[],int len)
{
   for(int i =0;i<len;i++)
   {
      cout<<a[i]<<",";
   }
   cout<<endl;
}
int main()
{
   int  a[5]={3,4,5,1,2};
   cout<<"before sort"<<endl;
   printfn(a,5);
   Sort(a,5);
   cout<<"after sort"<<endl;
   printfn(a,5);
   
   
   string  s[5]={"apple","dog","bee","funny","interset"};
   cout<<"before sort"<<endl;
   printfn(s,5);
   Sort(s,5);
   cout<<"after sort"<<endl;
   printfn(s,5);
   
   return 0;
}

结果:

sice@sice:~$ ./a.out 
before sort
3,4,5,1,2,
after sort
1,2,3,4,5,
before sort
apple,dog,bee,funny,interset,
after sort
apple,bee,dog,funny,interset,

函数模板深入理解

编译器从函数模板通过具体类型产生不同的函数
编译器会对函数模板进行连接
对模板代码本身进行编译
对参数替换后的代码进行编译
例子:

#include <iostream>
#include <string>
using namespace std;

/*class Test
{
    Test(const Test&);
public:
    Test()
    {
    }
};*/
template <typename T>
void Swap(T& a,T& b)
{
   T c = a;
     a = b;
     b = c;
}
typedef void(FuncI)(int&,int&);
typedef void(FuncD)(double&,double&);
//typedef void(FuncT)(Test&, Test&);
int main()
{
   FuncI* pi = Swap;
   FuncD* pd = Swap;
   // FuncT* pt = Swap;    // 编译器自动推导 T 为 Test
   cout<<"pi="<<reinterpret_cast<void*>(pi)<<endl;
   cout<<"pd="<<reinterpret_cast<void*>(pd)<<endl;
   // cout << "pt = " << reinterpret_cast<void*>(pt) << endl;
   return 0;
}

结果:

sice@sice:~$ ./a.out 
pi=0x8048738
pd=0x804875a

虽然看上去pi,pd指向同一个函数但是它们的地址值却不同,这是因为编译器根据不同参数自动推导,接着Swap进行替换后编译,然后在把编译后的地址分别赋给p1,pd,被注释掉的部分是因为Test类的拷贝构造函数是私有的,所以在swap里面执行赋值拷贝操作会报错,但是pt也还是不同的地址

函数模板可以定义任意多个不同的类型参数

说明:

template <typename T1,typename T2,typename T3>
T1 Add(T2 a,T3 b)
{
   return static_cast<T1>(a+b);
}

int ret = Add<int,float,double>(0.12,0.88);

对于多参数函数模板,我们需要指出返回值的类型,并且返回值的类型为第一个参数,其他数据类型可指定或者不指定,编译器会从左向右匹配类型参数进行转化,
在这里插入图片描述
例子:

#include <iostream>
#include <string>
using namespace std;
template <typename T1,typename T2,typename T3>
T1 Add(T2 a,T3 b)
{
   return static_cast<T1>(a+b);
}
int main()
{
  int r1 = Add<int>(0.5,0.8);
  float r2 = Add<float>(0.5,0.8);
  double r3 = Add<double>(0.5,0.8);
  cout<<"r1 ="<<r1<<endl;
  cout<<"r2 ="<<r2<<endl;
  cout<<"r3 ="<<r3<<endl;
  return 0;
}

结果:

sice@sice:~$ ./a.out 
r1 =1
r2 =1.3
r3 =1.3

函数重载遇到函数模板

C++编译器优先考虑普通函数
如果函数模板可以产生一个更好的匹配,那么选择模板、
可以通过空模板实参列表限定编译器只匹配模板
说明:

int r1 = Max(1,2);//考虑普通函数
double r2 = Max<>(0.5,0.6);//限定编译器只匹配函数模板

例子:

#include <string>
#include <iostream>
using namespace std;
template <typename T>
T Max(T a,T b)
{    
     cout<<"T Max(T a,T b)"<<endl;
     return a>b?a:b;
}  
int Max(int a,int b)
{  
   cout<<"int Max(int a,int b)"<<endl;
   return a>b?a:b;
}
template <typename T>
T Max(T a,T b,T c)
{  
   cout<<"T Max(T a,T b,T c)"<<endl;
   T d = Max(a,b);
   return d>c?d:c;
}
int main()
{  
   int i = 1;
   int j = 2;
   cout<<Max(i,j)<<endl;//默认调用普通函数
   cout<<Max<>(i,j)<<endl;//显示调用
   cout<<Max(0.1,0.7)<<endl;
   cout<<Max(0.1,0.2,0.5)<<endl;
   cout<<Max('a',100)<<endl;  //函数模板不允许隐式转化,所以调用普通函数
   return 0;
}

结果:

int Max(int a,int b)
2
T Max(T a,T b)
2
T Max(T a,T b)
0.7
T Max(T a,T b,T c)
T Max(T a,T b)
0.5
int Max(int a,int b)
100
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值