C++ 模板
1. 模板的概念
- 模板是C++较新的技术,是实现代码重用的重要工具;
- 模板——是类(结构)或者操作进行类型参数化后的样板,是C++实现代码重用机制的一种工具;
- 模板的声明——由关键字template声明,模板描述了一个类或函数的通用形式,编译器以类型参数化来进行支持;
例如:
int abs(int value) //求整型的绝对值
{
return value>0?value:-value;
}
double abs(double value) //求double型的绝对值
{
return value>0?value:-value;
}
这两个函数,功能几乎一样,只是返回值和参数不一样。C++提供了函数模板的功能来简化我们对这种情况的表达:
template<class T>
T abs(T value)
{
return value>0?value:-value;
}
其中T代表函数模板中要使用的通用类型,在函数的调用过程中,T被具体化。
- C++中模板有两类:函数模板和类模板
- 类模板:表示一组类(称为模板类)
- 函数模板:操作不同类型的对象
2. 函数模板与模板函数
2.1 函数模板的声明
template <class Type>
返回类型 函数名(参数表)
{
//使用Type的函数体
}
或者
template<typename 类型参数>
返回类型 函数名(模板形参表)
{
//函数体
}
举个栗子:
#include <iostream>
using namespace std;
//函数模板:模板的定义
template<class T>
T my_max(T x,T y)
{
return (x>y)?x:y;
}
int main()
{
int a = 6,b=8;
float c = 8.5,d=2.2;
double e = 21.12,f = 145.98;
char g = 'z',h = 'm';
cout << my_max(a,b) <<endl; //根据传入的参数类型 实例化模板函数max(int,int)
cout << my_max(c,d) <<endl; //根据传入的参数类型 实例化模板函数max(float,float)
cout << my_max(e,f) <<endl; //根据传入的参数类型 实例化模板函数max(double,double)
cout << my_max(g,h) <<endl; //根据传入的参数类型 实例化模板函数max(char,char)
return 0;
}
2.2 由函数模板生成模板函数
- 函数模板不是函数,不对应可执行代码。其定义是对一类函数的描述,只有实例化为模板函数后才能被执行。
- 函数模板实例化时,编译系统根据函数调用时的实参类型,生成与之相对应的重载函数即模板函数。
- 带有template的内容是函数模板,编译器生成的是模板函数。
举两个例子
求幂
#include <iostream>
using namespace std;
template <class T>
T Power(T a,int exp) //求幂
{
T ans = a;
while(--exp>0) ans *= a;
return ans;
}
int main()
{
cout <<"3 ^ 5= " <<Power(3 ,5) <<endl;
cout <<"1.1^2= " <<Power(1.1,2)<<endl;
return 0;
}
顺序查找
#include <iostream>
using namespace std;
template <class T>
int sequentialSearch(T a[],const T&k,int n)
{
int i=0;
while(k!=a[i] && i<=n-1) i++;
if(i>n-1) i=-1;
return i;
}
int main()
{
int i[] = {3,2,5,0,-1,7};
double d[] = {3.3,2.1,0.3,1.5,10.6,5.2};
char * c = "zju";
cout << sequentialSearch(i,15,6) <<endl;
cout << sequentialSearch(d,3.3,5) <<endl;
cout << sequentialSearch(c,'j',3) <<endl;
return 0;
}
2.3 模板函数重载的调用顺序
在C++中,函数模板与同名的非模板函数重载时,调用的顺序遵循下述约定:
- 寻找一个参数完全匹配的函数,如果找到就调用它。
- 寻找一个函数模板,将其实例化,产生一个匹配的模板函数。若找到了,就调用它。
- 若 1和2 都失败,寻找低一级的对函数的重载方法。例如通过类型转换可产生参数匹配等,若找到了,就调用它。
- 若 1,2,3 均未找到匹配的函数,则是一错误的调用。如果在第1步有多于一个的选择,那么这个调用是意义不明确的,是一个错误调用。
2.4 函数模板的异常处理
实例化T的各模板实参之间必须保持完全一致的类型,否则会发生错误。例如:
template<class T>
T my_max(T x,T y)
{
return (x>y)?x:y;
}
void fun(int a,char c){
my_max (a,a);
my_max (c,c);
my_max (a,c); //error
my_max (c,a); //error
}
错误原因:编译器无法根据不一致类型生成相应模板函数。
- 解决这个问题有以下两种方法:
- 采用强制类型转换,如:
my_max(i, int(c))
; - 用非模板函数重载函数模板,即定义一个完整的非模版函数 如:
int my_max(int i, char c) { … }
- 采用强制类型转换,如:
3. 类模板和模板类
3.1 类模板的定义
template <class Type>
class ClassName
{
//类的定义体
};
或
template <typename Type>
class ClassName
{
//具体内容
};
调用形式: ClassName<类型实参表> object
;
模板类是类模板对某一特定类型的实例。类模板代表了一类类,模板类表示某一具体的类。