当我们写交换两个变量内容的函数时,虽然有函数重载,但函数内容基本上都一样,这样显得有些麻烦了。C++ 中就提供了解决该问题的方法------模板。
泛型编程
泛型编程指的是编写与类型无关的通用代码,不指定具体数据类型,等到使用时再确定其类型。
泛型编程出现的意义就在于,使一段代码可以适配多种不同类型的数据,让我们不用重复写多个同样的代码。比如说对于不同类型数据的 Swap 交换函数,C++ 中就是靠模板来实现的。## 函数模板
函数模板定义的是一类函数,这类函数与参数类型无关,只有在特定情况下才会产生对应的函数。
一个函数模板定义的方式如下:template <typename 类型名(自定)>,其中类型的名称我们可以自己命名,通常是 T。
比如说我们定义函数 Swap 的函数模板:
cpp
//函数模板
template <typename T>
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
这就是一个函数模板了,当我们显式调用它或者传参调用该函数时,编译器才会根据该函数模板生成对应的函数。该过程就称为函数模板的实例化。
cpp
int main()
{
int x1 = 1,x2 = 2;
//显式实例化
//函数名后跟<>,内写类型
//显式实例化的时候就调用该函数
Swap<int>(x1, x2);
double d1 = 3.14, d2 = 5.12;
//隐式实例化
Swap(d1, d2);
return 0;
}
这是正常情况下,如果我们传过去的一个是 int 型,一个是 double 型,那么在类型匹配上都会产生冲突,第一个 T 匹配的是 int,第二个 T 变成了 double,产生了冲突,这时分两种情况:
- 如果是隐式实例化,此时编译器就会报错,不会进入实例化阶段。
- 如果我们已经显式实例化出了一个函数(如上),然后在显式实例化时就调用该函数(反之报错),编译器在传参时就会帮我们进行类型转换(
double转为int),再调用这个函数。
函数调用优先级
如果我们已经定义了一个 Swap 函数,假设就是 int 类型,然后我们显式实例化出了一个 int 型的 Swap 函数,那么编译器会先调用哪个函数呢?
cpp
void Swap(int& left, int& right)
{
int tmp = left + 3;//做点区别
left = right;
right = tmp;
}
//函数模板
template <typename T>
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
int main()
{
int x1 = 1, x2 = 2;
Swap<int>;
Swap(x1, x2);
cout<< x1 << ' ' << x2 << endl;
return 0;
}

可以看到调用的是我们自己定义的函数,如果模板函数更匹配,编译器则会去调用模板函数。## 类模板
类模板的定义格式如下:template <class T>
比如说我们想定义不同的 Stack 类来存储不同类型的数据:
cpp
//类模板
template <class T>
class Stack
{
public:
Stack(int n = 4)
:_array(new T[n])
, _top(0)
, _capacity(n)
{
}
void Push(T& x)
{
_array[_top++] = x;
}
~Stack()
{
delete[] _array;
}
private:
T* _array;
int _top;
int _capacity;
};
然后我们就可以生成不同的 Stack 类,存储不同类型的数据了。
cpp
int main()
{
Stack<int> s1;
Stack<char> s2;
Stack<double> s3;
//这三个对象是不同的类型
return 0;
}