这篇博客主要是简单的介绍函数模板和类模板的使用。
函数模板
假设现在需要你写多个交换函数用于各种类型的交换,如果一个一个写的话那即浪费时间,也会让代码整体不好看。
那么为了解决这种情况就可以使用函数模板。
例如下面:
using namespace std;
// 模板函数
template<class T>//如果你需要的模板函数具有多个参数
//则中间使用,分割例如:template<class T,class T1,1>
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}//函数模板
int main()
{
int a = 2, b = 3;
double a1 = 2.3, b1 = 3.3;
Swap(a, b);
cout <<"a = " << a << " " <<"b = " << b << endl;
Swap(a1, b1);
cout<<"a1 = " << a1 << " " <<"b1 = " << b1 << endl;
return 0;
}
那么这里为什么能够运行呢?在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码,对于整型类型也是如此。
这里需要注意的是函数模板和模板函数是两个不同的概念,函数模板是我们写给编译器的,函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
而模板函数则是编译器通过函数模板生成的函数。如下图
中间的是函数模板,而右边的便是编译器生成的模板函数。
函数模板的实例化
函数模板的实例化分成两种,一种位隐式实例化,一种为显示实例化。
隐式实例化:也就是让编译器通过实参推演模板参数的实际类型,例如一开始的交换函数。
显示实例化
以一个加法函数为例子
template<class T>
T Add(const T& a,const T& b)
{
return a + b;
}//函数模板
这里可以使用隐式实例化但是如果我传入的实参一个是整型一个是浮点型,如果不做任何处理是会直接报错的,那么要怎么处理呢?
第一种方法也就是强制类型转换,即要么将整型转为浮点型,要么将浮点型转化为整型。
第二种方法也就是显示实例化即让我们自己告诉编译器要实例化成哪种模板函数。
例如下面这样
int main()
{
int a = 2, b = 3;
double a1 = 2.3, b1 = 3.3;
cout << "a+b1 = " << Add<int>(a, b1) << endl;
return 0;
}
这里因为进行了强制类型转换,所以会将小数删除。
模板参数的匹配原则
假设你不仅写了一个int的Add函数也写了一个Add函数的模板,那么在不同的情况下编译器会如何匹配呢?
template<class T>
T Add( const T& a, const T& b)
{
return a + b;
}//函数模板
int Add(int a, int b)
{
cout << "调用了现存的函数" << endl;
return a + b;
}//具体的加法函数
int main()
{
int a = 3, b = 5;
cout<<"a+b = " << Add(a, b) << endl;//整型
double a1 = 2.23, b1 = 3.35;
cout << "a1+b1 = " << Add(a1, b1) << endl;//浮点型
return 0;
}
可以看到对于int类型的加法,编译器直接调用了现存的函数,而不是自己生成。而对于double则是自己生成的。
由此可以得出结论:对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
如果这里你强制要编译器自己写一个函数用于int加法,那么直接使用显示实例化即可。
最后还有一点需要注意:模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
类模板
既然一个函数可以有模板那么同样的一个类自然也可以用模板。需要注意的点就是类模板在使用的时候必须显示实例化。下面是一个栈的模板。
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>//如果需要定义多个T方式和函数一样,分割即可
class Vector
{
public:
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
// 使用析构函数演示:在类中声明,在类外定义。
~Vector();
void PushBack(const T& data);
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()//不能Vector::~Vector()这么写因为Vector不是类名而只是编译器生成时使用的模具类名
//Vector<T>这样的显示实例化后才是类名。
{
if (_pData)
delete[] _pData;
_size = _capacity = 0;
}
template <class T>
void Vector<T>::PushBack(const T& data)
{
_pData[_size] = data;
_size++;
}
int main()
{
Vector<int> s1;//使用的时候必须显示实例化
s1.PushBack(1);
s1.PushBack(2);
s1.PushBack(3);
return 0;
}
这篇博客只是简单的介绍了模板的简单应用,希望能对您有所帮助,感谢如果发现任何错误,欢迎指出。