1. 模板的概念:
模板是实现类属机制的一种工具,它的功能非常强,它是无约束类属机制和约束类属机制的集合。 它可以让用户构造模板函数。
模板,对象, 函数之间的关系见下图:
2.函数模板与模板函数:
先见下例:
#include <iostream.h>
template<class T> //模板声明
T max(T x,T y) //定义模板
{
return (x>y)? x:y;
}
main()
{
int i=10,j=56;
float x1=50.34, x2=56.34;
double y1=673.36, y2=465.972;
cout<<"the max of i, j is:"<<max(i, j)<<"/n";
cout<<"the max of x1, x2 is:" <<max(x1,x2)<<"/n";
cout<<"the max of y1, y2 is:" <<max(y1,y2)<<"/n";
return 1;
}
上面的这个程序虽然只是实现一个很简单的比较大小的问题, 但如果不用模板, 而用我们以前的方法, 由于参数类型和返回值类型不同将需要三个函数来实现, 这样是十分麻烦的。模板成功的解决了这个问题, 程序中生成了三个模板函数, 其中max(i,j)用模板实参int将类型实参数T进行了实例化;max(x1,x1)用模板实参float将类型参数T进行了实例化;max(y1,y2)用模板实参double将类型参数T进行了实例化。
从上面的例子我们可以看出, 函数模板提供了一类函数的抽象, 它以任意类型T为参数及函数返回值。由一个函数模板产生的函数称为模板函数, 它是函数模板的具体实例。
函数模板和模板函数的关系图:
要提醒大家注意的一点是:虽然模板参数T可以实例化成各种类型, 但是采用模板参数T的各种参数T的各参数之间必须保持完全一致的类型。例如:
T max(T x,T y)
{
return (x>y)?x:y;
}
void func(int i, char c, float f)
{
max(i,i); //正确
max(c,c); //正确
max(c,i); //错误
max(f,i); //错误
}
解决上面的问题就需要引入一个型的概念—重载, 既可以用非模板函数重载一个同名的函数模板。 重载有两种表述方式:
1.利用函数模板的函数体
使用次方法是必须注意各模板参数的实参类型必须一致。例如:
int max(int,int)
2.重新定义函数体
对于要重新定义函数体的重载函数, 所带参数的类型可以随意, 就象一般的重载函数一样定义。
定义重载函数特别要注意避免产生预期的和非预期的二义性。例如:
int max(int, int);
char max(int x,char y)
{
//......
}
当进行函数调用时有这样一个调用形式:
msx(i,f);
此处i为int,f为float,这个函数调用就存在着二义性。这时的调用就需要按照一定的规则安排先后顺序, 这些规则就是函数模板与同名的非模板函数的重载方法均遵循的约定:
(1)寻找一个参数完全匹配的函数, 如果找到了就调用它。
(2)在(1)失败后, 寻找一个函数模板, 使其实例化, 产生一个匹配的模板函数, 若找到了,就调用它。
(3)在(1)(2)均失败后, 再试一试低一级的对函数的重载方法,例如通过类型转换可产生参数匹配等, 若找到了,就调用它。
(4)若(1)(2)(3)均失败, 则这是一个错误的调用。
学习注:函数重载可以对参数进行隐式转化,但一旦加入了template,参数类型不匹配就会产生问题,如定义float max(float x,float y)
{
cout<<"don't use template"<<endl;
return (x>y)?x:y;
}
如果没有template,则调用max(float ,int)不会有问题,编译器会对int 进行转化,如果加入了template,则会出现错误。说明template对参数类型的要求更严格。
3.类模板与模板类
类模板与模板类的概念
一个类模板可以让用户为类定义一种模式, 使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能取任意类型。
定义一个类模板, 一般由两方面的内容:
(1)首先要定义类, 其格式为:
template<class T> //声明一个模板
class name
{
//....
}
name为类名,在类定义体中如采用数据类型的成员, 函数参数的前面需加上T。例如:
template<class T>
class vector
{
T * data;
int size;
pulic:
vetor(int);
T&operator[](int);
//...
};
(2)在类定义体外定义成员函数时, 若此成员函数中有模板参数存在, 则需在函数体外进行模板声明, 并且在函数名前的类名后缀上“<T>”.例如:
template<class T>
vector<T>::vector(int i)
{
//....
}
template<class T>
T&vector<T>::operator[](int i)
{
//....
}
类模板的使用
类模板的使用实际上是将类模板实例化成一个具体的类, 它的格式为:
类名<实际的类型>
例如:
main()
{
vector<int>x(5);
for(int i=0; i<=5;i++)
x[i]=i;
for(i=0;i<5;++i)
cout<<x[i]<<"";
cout<<"/n";
}
类模板和模板类之间的关系, 如图:
类模板使用需要注意的几点:
(1)在每个模板定义之前, 不管是类模板还是函数模板, 都需要在前面加上模板声明:
template<class T>
(2)类模板和结构模板在使用时, 必须在名字后面缀上模板参数<T>,如:list<T>,node<T>.
学习注:计算机是懒人的好朋友,template的产生,可以减少代码量,增加代码的复用。c++是由类和函数构成的。template也可以自动生成相应的类和对象。关于使用template的具体语法,也要注意。注意:template对参数类型的要求很严格,不支持隐式的参数类型转化。对于参数类型不同的使用场合,可以使用函数重载。