1、什么是模板?
模板是泛型编程的基础,泛型编程是指编写与类型无关的逻辑代码,是一种复用的方式。分为模板函数和模板类。
2、模板函数、模板类
1)模板函数
模板是与类型无关的编程,模板函数的基本使用方法是:
template <class 形参名1, class 形参名2, class 形参名n>
返回类型 函数名(参数列表)
{...}
注:class还可以用typename
例如:在没学习模板函数前,当我们要交换两个数据时,如果是整型,要定义一个整型的交换函数;如果又有别的类型的数据要进行交换,就要在定义上面定义的交换函数的重载,...这样,我们就要定义很多这样的函数。
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
return;
}
void Swap(float& a, float& b)
{
float tmp = a;
a = b;
b = tmp;
return;
}
但如果使用模板函数就可以不用这样麻烦:
template <class T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
return;
}
int main()
{
int a = 2;
int b = 8;
printf("a=%d b=%d\n", a, b);
Swap(a, b);
printf("a=%d b=%d\n", a, b);
float c = 1.234;
float d = 34.8;
printf("c=%f d=%f\n", c, d);
Swap(c, d);
printf("c=%f d=%f\n", c, d);
system("pause");
return 0;
}

可以看到,已经交换成功了。那么它的原理究竟是什么呢?
我们来看一张推演图:
结论(原理):编译调用模板函数时,编译器会自动推演出传递参数的类型,并自动生成相应的代码。
模板函数的重载:
template <class T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
return;
}
template <class T>
void Swap(T* a, T* b)
{
T tmp = *a;
*a = *b;
*b = tmp;
return;
}
int main()
{
int a = 2;
int b = 8;
Swap(a, b);
Swap(&a, &b);
system("pause");
return 0;
}
2)模板类
类模板的格式:
template<class 形参名1, class 形参名2, ...class 形参名n>
class 类名
{ ... };
模板类的原理:
模板参数--实现容器适配器
以实现栈为例:
定义容器适配器格式为:
template <class T, class Container>
template <class T, class Container = SeqList<T > > // 缺省参数
template <class T, class Container=Vector<T> >
class Stack
{
public:
void Push(T x)
{
_con.PushBack(x);
return;
}
void Pop()
{
_con.PopBack();
return;
}
const T& Top()
{
return _con.Back();
}
int Empty()
{
return _con.Size() == 0 ? 1 : 0;
}
protected:
Container _con;
};
模板的模板参数--实现容器适配器
在上面的例子中,如果在定义对象时,传入的参数T与Vector的T不一致,就会编译出错。为了避免这样的问题,出现了模板的模板参数的概念:
template <class T, template <class> class Container = SeqList > // 缺省参数
它的原理如下:
3、非类型模板参数
非类型模板参数,顾名思义,就是不是类型的模板参数,通常是一个常量,在静态顺序表中可以用来定义表的最大长度;
//template<typename T, size_t MAX_SIZE>
template <typename T, size_t MAX_SIZE = 10> //带缺省模板参数
class SeqList
{ p
ublic :
SeqList();
private :
T _array [MAX_SIZE];
int _size ;
};
template <typename T, size_t MAX_SIZE>
SeqList <T, MAX_SIZE>::SeqList ()
: _size(0)
{}
void Test ()
{
SeqList<int > s1;
SeqList<int , 20> s2;
}
也可以作为函数的参数,比如一个数总是要加上一个常数,就可以使用非类型的模板参数:
template <class T, int value>
T Add (const T& x )
{
return x + value;
}
值得注意的是,非类型的模板参数不能是浮点型或自定义类型。
4、模板的分离编译
当我们在使用模板时,如果他的声明和定义是一起的,那并不会有什么问题:
//test.h
#pragma once
#include <iostream>
using namespace std;
template <class T>
void hello(T x)
{
cout << x << endl;
cout << "hello" << endl;
}
//main.cpp
#include "test.h"
int main()
{
hello(1);
system("pause");
return 0;
}

可若是声明与定义分离,即不在一个文件,会怎么呢?
//test.h
#pragma once
#include <iostream>
using namespace std;
template <class T>
void hello(T x);
//test.cpp
template <class T>
void hello(T x)
{
cout << x << endl;
cout << "hello" << endl;
}
//main.cpp
#include "test.h"
int main()
{
hello(1);
system("pause");
return 0;
}
我们发现会编译不能通过:
这是为什么呢?
答:模板不支持分离编译。
那么怎么解决这个问题呢?
- 不要使定义和声明分离
- 将含有定义的.cpp文件包含进main.cpp中
- 模板的显示实例化
类模板的显示实例化:在类声明的后面加上template class 类名<T的类型>;
函数模板的显示实例化:在函数声明(以上面的例子为例)后面加上template void hello<int> ();