C++模板
泛型编程
泛型编程:编写与类型无关的逻辑代码,是代码复用的一种手段。模板是泛型编程的基础
函数模板
模板分类:函数模板、类模板
概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
函数模板格式
template < typename T1, typename T2,……,class Tn>返回值类型 函数名(参数列表){}
注:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替typename)建议尽量使用typename
函数模板还可以被定义为inline类型的:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前
template<class T>
inline T Add(const T& left, const T& right)
{
return left + right;
}
函数模板实例化&参数推演
模板它本身不是类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化
从函数实参确定模板形参类型和值的过程称为模板实参推演,多个类型形参的实参必须完全匹配
模板函数的编译
1 . 实例化之前,检查模板代码本身,是否出现简单语法错误,如:模板参数列表少了typename
2 . 在实例化后,检查模板生成的代码,是否所有的调用都有效,如:实例化类型不支持某些函数调用
类型转化
一般不会转换实参以匹配已有的实例化,相反会产生新的实例,编译器只会执行两种类型转换:
1 . const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用
2 . 数组或函数的转换:数组实参将转换为指向其第一个元素的指针函数实参将转换为当做指向函数类型的指针
模板参数
函数模板有两种类型参数:模板参数和调用参数
类型参数
1 . 模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则
2 . 模板形参的名字在同一模板形参列表中只能使用一次
3 . 所有模板形参前面必须加上class或者typename关键字修饰
4 . 在函数模板的内部不能指定缺省的模板实参
非类型形参
非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数
template<class T, size_t N>
void PrintArr(const T(&arr)[N])
{
for (size_t i = 0; i < N; ++i)
cout << arr[i] << " ";
cout << endl;
}
注意事项:
1 . 形参表使用<>括起来
2 . 和函数参数表一样,有多个参数时必须用逗号隔开,类型可以相同也可以不相同
3 . 定义模板函数时模板形参表不能为空
4 . 模板形参可以是类型形参,也可以是非类型新参,类型形参跟在class和typename后
5 . 模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型或自定义类型使用方法完全相同,可用于指定函数形参类型、返回值、局部变量和强制类型转换
6 . 模板形参表中,class和typename具有相同的含义,可以互换,使用typename更加直观。但关键字typename是作为C++标准加入到C++中的,旧的编译器可能不支持
函数模板重载
(函数的所有重载版本的声明都应该位于该函数被调用位置之前)
1 . 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
2 . 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调动非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
3 . 显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用, 而且所有的模板参数都应该根据实参演绎出来。函数名后跟<>来优先调用模板。即参数实例化。
4 . 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
函数模板特化
(很少用,主要类的特化)
函数模板特化形式如下:
1 . 必须要现有一个基础的函数模板
2 . 关键字template后面接一对空的尖括号<>
3 . 函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参函数形参表: 必须要和模板函数的基础参数类型完全相同
注意:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类
型完全匹配,如果不匹配,编译器将为实参模板定义中实例化一个实例
类模板
template <class T>
class Vector{ //只给出了构造与拷贝构造
public:
Vector(const T* array,size_t size)
:_array(new T[size])
,_capacity(size){
for (size_t i = 0; i < size; ++i){
_array[i] = array[i];
}
_size = size;
}
Vector(const Vector<T>& s);
private:
T* _array;
size_t _capacity;
size_t _size;
};
template <class T>
Vector<T>::Vector(const Vector<T>& s)
:_array(new T[s._size])
, _capacity(s._capacity)
, _size(s._size){
for (size_t i = 0; i < s._size; ++i){
_array[i] = s._array[i];
}
_size = s._size;
}
类模板特化
特化分为:全特化和偏特化,其都是在已定义的模板基础之上,不能单独存在。
模板分离编译
在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加template class SeqList; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者(不推荐),将声明和定义放到一个文件 “xxx.hpp” 里面,推荐使用这种方法。
模板的优缺点
优点
1 . 模板复用了代码,节省资源,C++的标准模板库(STL)因此而产生
2 . 增强了代码的灵活性
3 . STL里边map/set通过模板,底层采用红黑树实现。
缺点
1 . 模板让代码变得凌乱复杂,不易维护,编译代码时间变长
2 . 出现模板编译错误时,错误信息非常凌乱,不易定位错误
上一篇: UITextView 添加占位提示文字
下一篇: C++之模板