函数模板
代表着一组函数,只是该组函数的参数类型以及返回值类型可以变化。
template<class T>
T max(T a[],int n){
T max;
for(int i=1;i<n;i++){
if(max<a[i]){
max=a[i];
}
}
return max;
}
//其中类型形参名T同时被用在了函数模板max的返回类型和形参表中,它们代表同一个(可变化)的类型。
//参数T的实参应该是一个可以进行">"的基本类型,或已重载了运算符">"的用户自定义类型
函数模板说明
template <<模板参数表>><函数定义>;
函数定义
<返回类型><函数名>(<参数表>){<函数体>};
- 函数模板可以像一般函数那样直接使用,用户只需要给出具体的实参,而系统则根据实参信息确定出函数模板的各"参数名"所对应的具体类型,从而将其实例化为一个具体的函数,而后再去调用执行。
- 系统处理
1. 搜索程序说明中是否有参数表恰与max(a,i)之参数表完全相同的同名函数,如果有,就调用此代码;否则执行下一步。
2.检查是否有函数模板,且可经实例化成为参数匹配的同名函数,如果有,调用此实例化的模板函数代码付诸执行,否则执行下一步。
3.检查是否有同名函数,可经参数的自动转换后实现参数匹配,如果有,调用此函数代码付诸执行。
函数模板也可以重载,条件是两同名函数模板必须有不同的参数表。
C++编译器每次遇到函数模板调用时,要生成所需的每一个实例函数的源代码,所以函数模板不能缩短目标程序且不能提高运行效率!
类模板
类模板的概念和说明
类模板是类的抽象,类模板本身不是类,而是用来生成类的配方。一个类模板说明可以定义出具有共性(除类型参数或普通参数外,其余均相同)的一组类。
类模板的说明就是一个带有模板参数的类定义,其格式为:
template <<模板参数表>> class <类模板名> {<类模板定义体>};
template <class T,int i>
class TestClass{
T buffer[i];
...
};
类模板的基本使用方法
- 类模板的实例化。不能使用类模板来直接生成对象,因为其类型参数是不确定的,故须首先对模板参数指定“实参”,实例化的形式为:
<类模板名> <<>具体实参表>
TestClass<char,10>
TestClass<double,8>
TestClass<double,8>dobj1,dobj2
也可以用由用户定义的类(类型)来进行实例化
TestClass <complex, 15>
TestClass <point, 20>
- 类模板的成员函数既可以在类体内进行说明(自动按内联函数处理),也可以在类体外进行说明(定义)。在类外定义类模板的成员函数时(类模板的成员函数一般是一个函数模板),其定义格式类似于函数模板的定义。另外要记得在函数名前加上类限定符。一般格式如下:
template <模板参数表> 函数类型 类模板名 <模板函数参数表>::成员函数名(函数参数表){...};
template<class T,int i>
T TestClass<T,i>::getData(int j){
...//成员函数的函数体
};
其中的TestClass<T, i>::
所起的作用正是在类体外定义成员函数时在函数名前所加的类限定符。
- 类模板的参数。类模板是对于类的进一步抽象,被称为类属类。可以说是类的类。模板的定义体现了这个类集合中的类的共性,而模板参数则体现出集合中各个类的区别。类模板可以有两个或两个以上的类型参数。
- 类模板的设计一般是首先设计一个特定的类,然后再把它扩展成一个类模板。(类似代码重构)
关于类模板的几点说明
静态成员及友元
类模板的静态成员
类模板也允许有静态成员,实际上,它们是类模板之实例化类的静态成员。
也就是说,对于一个类模板的每一个实例化类,其所有的对象共享其静态成员。
template<class T>
class CA{
static T t;
};
CA<int>aiobj1,aiobj2;
CA<char>acobj1,acobj2;
类模板的友元
类模板定义中允许包含友元。我们讨论类模板中的友元函数,因为说明一个友元类,实际上相当于说明该类的所有成员函数都是友元函数。
- 该友元函数为一般函数,则它是类模板所有实例化类的友元函数
- 该友元函数为一函数模板,但其类型参数与类模板的类型参数无关,则该函数模板的所有实例化(函数)都是类模板的所有实例化类的友元。
- 更复杂的情况是,该友元函数为一函数模板,且它与类模板的参数类型有关。例如,函数模板可以用该类模板作为其函数参数的原型。
类模板的类型参数的错误检测
类模板的类型参数往往在实例化时不允许用任意的类(类型)作为“实参”。
C++没有对模板的实参类型进行检查的机制,它仅仅是通过实际操作发生语法错误时才能指出实例化的错误。
模板的实参不当主要在实例化后的函数成员调用中体现出来,如:
template<class T>
class stack{
T num[MAX];//num中存放栈的实际数据
int top;//top为栈顶位置
public:
stack(){top=0;}
void push(T a){num[top++]=a;}//将数据a"压入栈顶"
void showtop(){
if(top==0)cout<<"stack is empty!"<<endl;
else cout<<"Top_Member:"<<num[top-1]<<endl;
}
};
其中的showtop()
要求T类型的数据必须为可直接通过运算符>>
来显示的数据。stack<complex>com1,com2;
执行com1.showtop()
会出问题。
解决措施:
- 对类complex追加插入运算符
>>
的重载定义。 - 在类模板stack的定义中增加一个特例版本的定义。
void stack<complex>::showtop(){
if(top==0) ...
else{
...
}
}
当处理某一类模板中的可变类型T型数据时,如果处理算法并不能对所有的T类型取值做统一的处理,此时可通过使用专门补充的所谓特例版本来对具有特殊性的那些T类型取值做特殊处理。
- 也可以对函数模板,或类模板的个别函数成员补充其“特例版本”定义。
标准模板库STL
最主要与最常用的两部分分为容器类(container classes)和算法库(algorithm library)。
容器类是一些标准类模板的总称,其中包含标准类模板,如vector(向量)、list(列表)、deque(双端队列)、map(映像)、multimap(多重映像)、set(集合),以及multiset(多重集合)等。
算法库包括了各种基本算法,如sort、copy、search、reverse等函数模板。
C++11对于模板和标准模板库进行了一些扩展,例如外部模板、变长参数模板,以及标准程序库的变更。