目录
一、函数模板
1.概念
函数模板是一个函数家族,和参数的类型无关。和逻辑有关。如果不同的两个(或者多个)函数只是参数不一样,逻辑完全一致,那么可以写一个函数模板。在调用时,就不需要写多个函数重载。
2.格式
template < typename1 T1, typename2 T2, typename3 T3,...,typenameN Tn>
//写个swap交换
template <typename T>
void swap(T&a , T&b>
{
T tmp = a;
a = b;
b = tmp;
}
T也可以写成其他字母,我用T表示类型名字,只是它可以作为type的简写,容易读。
typename 是用于定义模板参数的关键字,可以用class代替,但是不能用struct代替。
3.原理
函数模板只是一个模具,它只有在调用的时候,才会实例化。
当给函数传参的时候,传的参数不一样,编译器会根据参数进行推演函数的参数类型应该是什么样,然后编译器自己写一份这样的函数给用户调用,所以并不是没有这份代码了,它就不存在了。只是把写重复的函数的代码的工作交给了编译器处理而已。
在汇编的时候,或者把函数的地址打印出来,可以看到不同参数调用同一份模板会产生不同的地址的。
4.实例化
使用函数模板调用函数的方式叫实例化。模板参数实例化分为隐式实例化和显式实例化两种。
隐式实例化:
编译器根据传的参数类型来推演将要实例化的实际参数类型。
传int 推int
传double 推double
template <typename T>
void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int m=1, n=2;
double p=1.00, q=2.00;
//隐式实例化
swap(m,n);
swap(p,q);
return 0;
}
显式实例化
显式实例化就是在调用的时候直接告诉编译器你的类型是啥。
template <typename T>
void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int m = 1,n = 2;
//显式实例化
swap<int>(m,n);
return 0;
}
如果显式实例化类型不匹配,编译器会尝试使用隐式实例化,如果转换失败,那么编译器会报错。
5.模板参数的匹配原则
a.模板函数和非模板同名函数可以同时存在,并且模板函数也可以被实例化成非模板函数
b.在其他条件都相同的情况,编译器优先调用非模板函数。不会自己再写一个多余的。
c.如果类型不匹配,非模板函数可以自动类型转换,但是模板函数不会进行自动类型转换。这种情况,如果模板函数和调用时更匹配,编译器会用模板函数实例化,而不会用非模板函数的类型转换。
二、类模板
template <class T>
class Vector
{
public:
Vector(int capacity = 10)
:_a = new T(capacity)
,_capacity(capacity)
,_size(0)
{}
~Vector();
T& operator[](size_t pos)
{
assert(pos < size);
return _a[pos];
}
//...
private:
T* _a;
int _capacity;
int _size;
}
template <class T>
Vector<T>::~Vector()//类的析构模板放在类外写,要加类名
{
if(_a)
delete[](_a);
_size=_capacity=0;
}
类模板实例化需要在类名后面加模板参数的类型。类名是类名。
Vector 是类名
Vector<int> 、Vector<T>才是类模板实例化。