前言:各位老铁好,今天来分享函数模板和类模板的知识,这个算是一个小知识,但这个小知识非常重要,相信学C++的各位老铁一定听过STL这个名词,那么STL是什么呢?它与我们今天分享的这个函数模板和类模板有什么关系呢?
首先我们要明白STL全称是Standard Template Library,是c++的标准模板库,顾名思义,STL既然是一个模板库了,今天我们将的就是模板,所以我们可以知道今天分享的内容是为后面学习STL打基础的,好了,接下来进入正题了。
函数模板
1.概念:函数模板就表示一个函数家族,里面存着的模板适用于各种类型的参数,所以函数模板与函数的参数类型无关,函数模板会根据实参传过来的类型进行生成对应的函数类型版本。
2.语法格式:
template<class T>//这个是函数模板必须要写的开头
//class关键字可以换成typename关键字,但不能换成struct关键字
void Swap(T& left, T& right)
{T tmp = right;right = left;left = tmp;
}
这个就是一个交换函数的模板,你可以传任何类型的数据进行交换。
我们来看看能不能实现交换吧
template<class T>//这个是函数模板必须要写的开头
//class关键字可以换成typename关键字,但不能换成struct关键字
void Swap(T& left, T& right)
{T tmp = right;right = left;left = tmp;cout << left << " " << right;cout << endl;
}int main()
{int a = 0, b = 1;Swap(a, b);double c = 1.1, d = 2.2;Swap(c, d);string ch1 = "abc";string ch2 = "def";Swap(ch1, ch2);return 0;
}
3.函数模板的实例化
用不同类型的参数使用函数模板表示对函数模板的实例化,函数模板的实例化分为显示实例化和隐式实例化。
隐式实例化:让编译器通过实参来判断参数的类型,从而生成相对应得代码(上面得例子就是隐式实例化)
显示实例化:在调用时,在函数名后的<>中指定模板参数的实际类型
我们来看看下面这个代码
template<class T>//这个是函数模板必须要写的开头
//class关键字可以换成typename关键字,但不能换成struct关键字
void Swap(T& left, T& right)
{T tmp = right;right = left;left = tmp;cout << left << " " << right;cout << endl;
}int main()
{int a = 0;char b = 'a';Swap(a, b);return 0;
}
可以猜猜这个代码是正常运行还是报错,我们来看看结果吧
编译器直接报错了,这是为什么呢?我们可以看到a和b是不同类型的数据,现在我们要拿这两个不同类型的数据进行实例化,但我们可以发现,Swap的参数只有一个T类型,编译器不知道是实例化成int类型还是char类型 ,所以编译器直接就报错了,有的老铁会说编译器不是会自动从低类型转化为高类型吗?的确编译器在算术运算时会从低类型转化为高类型,但在模板函数里面,编译器一般不会进行隐式类型的转化,毕竟转化出了问题,编译器就要背锅了。
那么我们如何能解决这个问题呢?有两种方法,一是直接进行强制类型转化,二是使用显示实例化
**强制转化类型**
template<class T>
T ADD(const T& left, const T& right)
{return left + right;
}int main()
{int sum = 0;int a = 1;double b = 1.1;sum = ADD(a, (int)b);cout << sum << endl;return 0;
}
**显示实例化**
template<class T>
T ADD(const T& left, const T& right)
{return left + right;
}int main()
{int sum = 0;int a = 1;double b = 1.1;sum = ADD<int>(a, b);//这里表示将a和b都当成int类型,编译器会自动把b转化为int类型cout << sum << endl;return 0;
}
4.模板函数的匹配原则
1.一个非模板函数可以和一个同名的模板函数同时存在,且该模板函数还可以实例化为非模板函数
template<class T>
T ADD(const T& left, const T& right)
{return left + right;
}
int ADD(const int a, const int b)
{return a + b;
}int main()
{int sum = 0;int a = 1;double b = 1.1;sum = ADD(a, (int)b);//这里表示将a和b都当成int类型,编译器会自动把b转化为int类型cout << sum << endl;return 0;
}
对于同名模板函数和非模板函数,如果其他条件相同,编译器会优先调用非模板函数,这样毕竟效率高。
类模板
1.类模板定义:
template<class T>
class Vector//vector不是具体的类,而是一个可以生成任何类型的类的模具
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}
private:T* _pData;size_t _size;size_t _capacity;
};
那么该如何在模板中实现在类中声明,在类外定义,其实也和非模板实现在类中声明,在类外定义差不多,
template<class T>
class Vector//vector不是具体的类,而是一个可以生成任何类型的类的模具
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();
private:T* _pData;size_t _size;size_t _capacity;
};//注意在类外定义时需要加模板参数列表
template<class T>
Vector<T>::~Vector()//Vector<T>才是类型
{if (_pData)delete[] _pData;_size = _capacity = 0;
}
2.类的实例化:
template<class T>
class Vector//vector不是具体的类,而是一个可以生成任何类型的类的模具
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();
private:T* _pData;size_t _size;size_t _capacity;
};//注意在类外定义时需要加模板参数列表
template<class T>
Vector<T>::~Vector()//Vector<T>才是类型
{if (_pData)delete[] _pData;_size = _capacity = 0;
}int main()
{//类模板实例化(只有实例化了才创建出一个真正的类)Vector<int> a;//Vector<int>才是类的类型,Vector只是类名return 0;
}
总结:
今天和大家分享的两个小知识,函数的模板和类的模板,它们有着共同点也有不同点,希望各位老铁看了这篇文章能有所收获。