本章学习:
1)初探函数模板
2)深入理解函数模板
3)多参函数模板
4)重载函数和函数模板
当我们想写个Swap()交换函数时,通常这样写:
void Swap(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量。更糟的是,还会增加可执行文件的大小.
所以C++引入了泛型编程概念
在C++里,通过函数模板和类模板来实现泛型编程(类模板在下章将讲解)
函数模板
比如:
template <typename T> //声明使用模板,并定义T是一个模板类型
void Swap(T& a, T& b) //紧接着使用T
{
T c = a;
a = b;
b = c;
}
当我们使用int类型参数来调用上面的Swap()时,则T就会自动转换为int类型.
函数模板的使用
例如,我们写了一个Swap函数模板,然后在main()函数里写入:
int a=0;
int b=1;
Swap(a,b); //自动调用,编译器根据a和b的类型来推导
float c=0;
float d=1;
Swap<float>(c,d); //显示调用,告诉编译器,调用的参数是float类型
初探函数模板
写两个函数模板,一个用来排序数组,一个用来打印数组,代码如下:
#include <iostream>
#include <string>
using namespace std;
template < typename T >
void Sort(T a[], int len)
{
for(int i=1;i<len;i++)
for(int j=0;j<i;j++)
if(a[i]<a[j])
{
T t=a[i];
a[i]=a[j];
a[j]=t;
}
}
template < typename T >
void Println(T a[], int len)
{
for(int i=0; i<len; i++)
{
cout << a[i] << ", ";
}
cout << endl;
}
int main()
{
int a[5] = {5, 3, 2, 4, 1};
Sort(a, 5); //自动调用,编译器根据a和5的类型来推导
Println<int>(a, 5); //显示调用,告诉编译器,调用的参数是int类型
string s[5] = {"Java", "C++", "Pascal", "Ruby", "Basic"};
Sort(s, 5);
Println(s, 5);
return 0;
}
运行打印:
1,2,3,4,5,
Basic,C++, Java,Pascal,Ruby,
深入理解函数模板
为什么函数模板能够执行不同的类型参数?
答:
试验函数模板是否生成真正的函数
通过两个不同类型的函数指针指向函数模板,然后打印指针地址是否一致,代码如下:
#include <iostream>
using namespace std;
template <typename T>
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
int main()
{
void (*FPii)(int&,int&);
FPii = Swap ; //函数指针FPii
void (*FPff)(float&,float&);
FPff = Swap ; //函数指针FPff
cout<<reinterpret_cast<void *>(FPii)<<endl;
cout<<reinterpret_cast<void *>(FPff)<<endl;
//cout<<reinterpret_cast<void *>(Swap)<<endl;
//编译该行会出错,因为Swap()只是个模板,并不是一个真正函数
return 0;
}
运行打印:
0x41ba98
0x41ba70
可以发现两个不同类型的函数指针,指向同一个函数模板,打印的地址却都不一样,显然编译器默默帮我们生成了两个不同的真正函数
多参数函数模板
在我们之前小节学的函数模板都是单参数的, 其实函数模板可以定义任意多个不同的类型参数,例如:
template <typename T1,typename T2,typename T3>
T1 Add(T2 a,T3 b)
{
return static_cast<T1>(a+b);
}
注意:
接下来开始试验多参数函数模板
#include <iostream>
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 a = add(1,1.5); //该行编译出错,没有指定返回值类型
int a = Add<int>(1,1.5);
cout<<a<<endl; //2
float b = Add<float,int,float>(1,1.5);
cout<<b<<endl; //2.5
return 0;
}
运行打印:
2
2.5
重载函数模板
函数模板可以像普通函数一样被重载
接下来开始试验重载函数模板
#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;
}
int main()
{
int a=0;
int b=1;
cout<<"a:b="<<Max(a,b) <<endl ; //调用普通函数 Max(int,int)
cout<<"a:b="<<Max<>(a,b)<<endl; //通过模板参数表 调用 函数模板 Max(int,int)
cout<<"1.5:2.0="<<Max(1.5,2.0)<<endl;
//由于两个参数默认都是double,所以无法隐式转换,则调用函数模板 Max(double,double)
cout<<"'a',100="<< Max('a',100)<<endl;
//由于100是int型,所以char型可以进行隐式转换,从而调用普通函数 Max(int,int)
return 0;
}
运行打印:
int Max(int a,int b)
a:b=1
T Max(T a,T b)
a:b=1
T Max(T a,T b)
1.5:2.0=2
int Max(int a,int b)
'a',100=100
未完待续,接下来下章来学习类模板