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