模板是C++支持参数化程序设计的工具,通过它可以实现参数多态性。所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。
函数模板
函数模板的定义形式是:
template <class T>或template <class T>
类型名函数名(参数表)
{函数体定义}
/所有函数模板的定义都是用关键字template 开始的,该关键字之后是使用尖括号<>括起来的类型参数表。每一个类型参数(T)之前都有关键字class或者关键字typename,这些类型参数代表的是类型,可以是内部类型或自定义类型。这样,类型参数就可以用来指定函数模板本身的参数类型和返回值类型,以及声明函数中的局部变量。函数模板中函数体的定义方式与定义其它函数类似。
实例一:
#include<iostream>
using name space std;
template<typenameT>
Tabs(Tx){
return x<0?-x:x;
}
int main(){
int n=-5;
double d= -5.5;
cout<<abs(n)<<endl;
cout<<abs(d)<<endl;
cout<<abs(d+n)<<endl;
system("pause");
return 0;
}
运行结果:
实例二:
#include<iostream>
using name space std;
template <typename T>
void outputArray(const T *P_aaray,const int count){
for(int i=0;i<count;i++)
cout<<P_aaray[i]<<" ";
cout<<endl;
}
int main(){
const int aCount = 8,bCount= 8,cCount = 20;
int aArray[aCount] = {1,2,3,4,5,6,7,8};
double Array[bCount] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
char cArray[cCount] = "Welcometo see you!";
cout<<"a Array contains: "<<endl;
outputArray(aArray,aCount);
cout<<"b Array contains: "<<endl;
outputArray(bArray,bCount);
cout<<"c Array contains: "<<endl;
outputArray(cArray,cCount);
system("pause");
return 0;
}
运行结果:
函数模板几点注意
① 如果在全局域中声明了与模板参数同名的对象函数或类型则该全局名将被隐藏在下面的例子中tmp 的类型不是double 是模板参数Type
typedef double Type;
template <class Type>
Type min( Type a, Type b )
{
// tmp 类型为模板参数 Type
// 不是全局 typedef
Type tmp= a < b ? a : b;
returntmp;
}
② 在函数模板定义中声明的对象或类型不能与模板参数同名
template <class Type>
Type min( Type a, Type b )
{
// 错误: 重新声明模板参数 Type
typedef double Type;
Type tmp= a < b ? a : b;
returntmp;
}
③ 模板类型参数名可以被用来指定函数模板的返回位
// ok: T1 表示 min() 的返回类型
// T2 和 T3 表示参数类型
template <class T1, class T2, class T3>
T1 min( T2, T3 );
④ 模板参数名在同一模板参数表中只能被使用一次,但是模板参数名可以在多个函数模板声明或定义之间被重复使用
// 错误: 模板参数名 Type 的非法重复使用
template <class Type, class Type>
Type min( Type, Type );
// ok: 名字 Type在不同模板之间重复使用
template <class Type>
Type min( Type, Type );
template <class Type>
Type max( Type, Type );
⑤ 如果一个函数模板有一个以上的模板类型参数则每个模板类型参数前面都必须有关键字class 或typename
// ok: 关键字 typename和 class 可以混用
template <typename T, class U>
T minus( T*, U );
// 错误: 必须是 <typename T, class U> 或 <typename T, typename U>
template <typename T, U>
T sum( T*, U );
⑥ 为了分析模板定义编译器必须能够区分出是类型以及不是类型的表达式对于编译器来说它并不总是能够区分出模板定义中的哪些表达式是类型例如如果编译器在模板定义中遇到表达式Parm::name 且Parm 这个模板类型参数代表了一个类那么name 引用的是Parm 的一个类型成员吗.
template <class Parm, class U>
Parm minus( Parm* array, U value )
{
Parm::name * p; // 这是一个指针声明还是乘法乘法
}
编译器不知道name 是否为一个类型因为它只有在模板被实例化之后才能找到Parm 表示的类的定义为了让编译器能够分析模板定义用户必须指示编译器哪些表达式是类型表达式告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字typename 例如如果我们想让函数模板minus()的表达式Parm::name 是个类型名因而使整个表达式是一个指针声明我们应如下修改
template <class Parm, class U>
Parm minus( Parm* array, U value )
{
typename Parm::name * p; // ok: 指针声明
}
关键字typename 也可以被用在模板参数表中以指示一个模板参数是一个类型
⑦ 如同非模板函数一样函数模板也可以被声明为inline 或extern 应该把指示符放在模板参数表后面而不是在关键字template 前面
// ok: 关键字跟在模板参数表之后
template <typename Type>
inline
Type min( Type, Type );
类模板
使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值能取任意类型(包括系统预定义的和用户预定义的)。由于类模板需要一种或多种类型参数,所以类模板也常称为参数化类。
类模板声明的语法形式是:
template<模板参数表>
class类名
{类成员声明}
如果需要在类模板以外定义其成员函数,则需要采用以下形式:
template<模板参数表>
类型名类名<T>::函数名(参数表)
“模板参数表”由用逗号分隔的若干类型标识符或常量表达式构成,其内容包括:
(1) class(或typename) 标识符,指明可以接受一个类型的参数。
(2) 类型说明符 标识符,指明可以接受一个由“类型说明符”所规定类型的常量作为参数。
“模板参数表”同时包含上述多项内容时,各项内容以逗号分隔。应该注意的是,模板类的成员函数必须是模板函数。
一个类模板声明自身不产生代码,他说明了类的一个家族。只有当它被其它代码引用时,模板才根据引用的需要产生代码。
使用一个模板类来建立对象时,应按如下形式声明:
模板<模板参数表> 对象名1,…,对象名n;
举例:
#include<iostream>
#include<cstdlib>
using name space std;
struct Student
{
int id;
float gpa;
};
template <classT>
class Store{
private:
Titem;
int haveValue;
public:
Store(void);
T GetElem(void);
void PutElem(Tx);
};
template <class T>
Store<T>::Store(void):haveValue(0){
}
template <classT>
T Store<T>::GetElem(void){
if (haveValue ==0)
{
cout<<"Noitem present!"<<endl;
exit(1);
}
returnitem;
}
template <class T>
void Store<T>::PutElem(T x){
haveValue++;
item = x;
}
int main(){
Student g ={1000,23};
Store<int> S1,S2;
Store<Student> S3;
Store<double> D;
S1.PutElem(3);
S2.PutElem(-7);
cout<<S1.GetElem()<<""<<S2.GetElem()<<endl;
S3.PutElem(g);
cout<<"Thestudent id is "<<S3.GetElem().id<<endl;
cout<<"Retrievingobject D "<<endl;
cout<<D.GetElem()<<endl;
system("pause");
return 0;
}