学习模版之前我们要学习泛型编程,泛型编程是一种代码风格。所谓泛型编程就是已经可能抽象的方式去编写程序的算法,使其不依赖于算法上的数据形式。泛型变成只编写与数据类型无关的通用代码,今天学习的就是泛型编程的基础。 模板分为:函数模板和类模板。

1.1. 函数模板的概念 函数模版是一个函数群体,它并不依赖于函数关系。当被使用时会被参数化,然后根据参数类型产生一个特定类型的函数版本。
template <typename T1,typename T2,typename T3……typename Tn>
返回值类型 函数名 (参数列表){ } //函数模板
template <class T>
void Swap(T& left,T& right)
{
T tmp = left;
left = right;
right = tmp;
}PS:typename是用来定义模板参数的关键字,也可以使用class,但是不能用struct替换class。
函数模板严格意义上来讲并不是真正的函数,它只是一个蓝图,是编译器来产生特定参数类型函数的模具。换句话说,就是通过这个函数模板,我们将本来我们应该做的事情交给了编译器。
我们这里通过一个Swap()函数来进行说明:
void Swap(int& left, int& right) {
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right) {
double temp = left;
left = right;
right = temp;
}
int main()
{
int a = 0;
int b = 1;
double c = 2.2;
double d = 3.3;
Swap(a, b);
Swap(c, d);
return 0;
}很明显,这段代码中我们使用到了函数重载,但是会有这么几个缺点:
那如果使用函数模板就会变成这样:
//Swap()函数
//template <typenname T>
template <class T>
viod Swap(T& left,int &right)
{
T tmp = left;
left = right;
right = tmp;
}这样的话,我们就解决了上面的两个问题:

在编译器阶段,函数模板的使用就是,编译器会根据传入的参数类型的,来生成一份对应类型的函数代码以供调用。
比如,当传入的类型是int时,编译器就会通过对实参类型的推演,将T类型确认为int,然后生成一份int类型的代码。
当不同的参数被传入函数模板时,就会变成函数模板的实例化,函数模板的实例化被分为显式实例化和隐式实例化:
隐式实例化就是懒到极致,直接让编译器去根据传入的参数推演:
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 20.0, d2 = 10.0;
Add(a1, a2);
Add(d1, d2);
return 0;
}但是我们来看下面这个代码:
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10;
double d1 = 20.0;
Add(a1, d1);
return 0;
}这个代码与上面的区别就是传入的参数并不是统一的:

这种情况编译器是推演不出来的,那么这个时候有两种解决方式,一是用户进行强制类型转换,二是显式实例化。
int main()
{
int a1 = 10;
double d1 = 20.0;
Add(a1, (int)d1);//用户自己来强制转换
return 0;
}template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10;
double d1 = 20.0;
Add<int>(a1, d1);//显示实例化成int
Add<double>(a1, d1);//显示实例化成double
return 0;
}就比如这个时候,我显式地将函数模板的参数类型设置为int、double,这个时候传入参数的时候就会根据已经显式规定的参数类型进行隐式类型转换,如果转换失败才会报错。

// 专门处理int的加法函数
int Add(int left, int right) {
return left + right;
}
// 通用加法函数
template<class T> T Add(T left, T right) {
return left + right;
}
int main()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); // 调用编译器特化的Add版本
return 0;
}// 专门处理int的加法函数
int Add(int left, int right) {
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right) {
return left + right;
}
int main()
{
//与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2);
//模板函数可以生成更加匹配的版本
//编译器根据实参生成更加匹配的Add函数
Add(1, 2.0);
return 0;
}template <class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};下面使用vector举一个例子:
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
// 使用析构函数演示:在类中声明,在类外定义。
~Vector();
void PushBack(const T& data);
void PopBack();
// ...
size_t Size() { return _size; }
T& operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
if (_pData)
delete[] _pData;
_size = _capacity = 0;
}
(本篇完)