1.模板的作用:
能够快速建立具有类型安全的类库集合和函数集合。模板可以让程序对任何其他数据类型进行同样范式的处理。
例如:
void swap(int& a, int& b){
int temp=a;
a=b;
b=temp;
}
|
void swap(double& a, double& b){
double temp=a;
a=b;
b=temp;
}
|
这两个函数目的都是对a,b进行交换,但是由于它们的类型不同,而写了两个几乎一样的程序,这是一种冗余,可以用模板来简化。
|
函数模板:
template<class T>void swap(T& a, T& b){
T temp=a;
a=b;
b=temp;
}
int main(){
int a=1;
int b=2;
double c=1;
double d=2;
swap(a,b); // 结果a=2,b=1 当编译器识别swap(a,b)的时候,它会产生一个模板函数swap(int&,int&){...};
swap(c,d); // 结果c=2,d=1 当编译器识别swap(c,d)的时候,它会产生一个模板函数swap(double&,double&){...};
}
|
注意:
函数模板和模板函数的区别:
函数模板:其实就是函数的一个定义,编译器没有实实在在为其产生任何执行代码。
模板函数:其实是函数模板的一个实例,编译器为参数为不同数据类型的函数调用创建不同的模板函数。
两者的关系其实和类与对象的关系相似。
|
再例如:
class Cat{
}
class Dog{
}
|
class CatList{
public:
List();
void add(Cat&);
void remove(Cat&);
Cat* Find(Cat&);
~List();
private:
....
}
|
class DogList{
public:
List();
void add(Dog&);
void remove(Dog&);
Cat* Find(Dog&);
~List();
private:
....
}
|
这两个类其实目的是要建立一条链表,链表操作一样,但是由于而结点类型的不同,写了两个几乎一样的程序,这是一种冗余,可以用模板简化。
|
类模板:
template <class T> class List{
public:
List();
void add(T&);
void remove(T&);
Cat* Find(T&);
~List();
private:
....
}
|
int main(){
Cat cat;
List<T> catList; // 当编译器识别List<T> catList的时候,它会产生一个模板类,class CatList{...},如上面的CatList定义所示;
catList.add(cat); // 当编译器识别List<T> dogList的时候,它会产生一个模板类,class DogList{...},如上面的DogList定义所示;
Dog dog;
List<T> dogList;
dogList.find(dog);
}
|
注意:
类模板和模板类的区别与函数模板和模板函数的区别相同。
|
2.函数模板比宏定义多一个类型检查:
#define max(a,b) ((a)>(b))?(a):(b) // 宏定义的a,b
没有类型检查机制,那么int a,char b也拿来比较,这会出错。
template <class T> T max(T& a, T&b){
return ((a)>(b))?a:b; //使用函数模板可
对不同的数据类型进行相应的模板函数匹配,那么它是
有类型检查的。
}
当然T类的>需要定义,因为
不是所有的类型都可以>比较
,例如char*类型就不能;等一下讲重载模板函数有涉及。
3.重载模板函数:
int max(int& a,int&b){
return ((a)>(b))?a:b;
}
float max(float& a, float& b){
return ((a)>(b))?a:b;
}
假如还没有相应的函数模板,那么如果输入的是字符呢?怎么处理呢?
如:
max('3','5'); // 结果是53(这是5的ASII码),因为没有相应的char max(char a,char b);所以会自动匹配int max(int,int),这样的话只会输出53.
假如有了下面的函数模板,那么
编译器在看到max('3','5');的时候,会
产生相应的模板函数char max(char,char);这样就可以输出所期望的较大值5.
函数模板为:
template <class T> T max(T& a, T&b){
return ((a)>(b))?a:b;
}
就算有了相应的函数模板,但是如果输入的是字符串呢?字符串是不可直接>比较的,而是需要函数strcmp(),所以还是没有相应的模板函数啊?
这时候就
需要重载模板函数。
char* max(char* a, char* b){
return (
strcmp(a,b)?a:b);
}
int main(){
max("Hello","Lawliet"); // 当
编译器看到max("Hello","Lawliet")的时候,首先
试图匹配重载函数,找到了匹配的char* max(char*,char*)那么就
不用
再寻找模板的匹配,那么就不会像上面的int max(int*,int*)那么会产生一段模板函数的代码。
}
4.模板(包括类的定义,以及类成员函数的声明和定义)应放在该头文件中。
原因是
1.类的定义和类成员函数的声明像普通函数一样要放在头文件中。
2.既然模板本身是一个“定义”,就是要被include的,所以它类成员函数的定义也必须明确地包含在头文件中,才能使得被include的时候可以使用。