一.为什么使用模板
1.C/C++是一种静态类型语言(预处理->汇编->编译->链接),好处是速度快,缺点是实现通用代码麻烦,例如:实现支持所有类型的快速排序.
2.借助函数重载实现通用代码,好处是实现简单,但代码段会增加
3.借助宏函数来实现通用代码,类型检查不严格
4.借助回调函数实现通用代码,使用麻烦
5.由于以上原因C++之父在C++实现了模板技术,让C++能够支持泛型编程
二.函数模板
1.函数模板的定义
template<typename 类型参数1,typename 类型参数2...>
返回值 函数名(参数类型)
{
return 返回值;
}
template<typename T>
T find(T* arr,size_t len)
{
return val;
}
可以使用任何标识符作为类型参数符,但使用'T'是约定俗成的,它表示调用这个函数时所指定的任意类型
2.函数模板的使用
C++编译的编译器并不是把模板编译成一个可以处理任意类型的单一实体,而是根据模板的使用者的参数,产生不同的实体
根据具体类型代替模板参数生成函数实体过程叫实例化
模板是在使用时才实例化,可以自动实例化,也可以手动实例化(在函数调用时函数名在小括号之间,加<类型参数>)
每个函数模板都会进行二次编译,第一次编译在实例化之前,检查模板代码本身是否正确,第二次是实例化过程中,结合所使用类型参数,再次检查模板代码.是否所有代码都有效
注意:第二次编译才会生成二进制指令,第一次编译仅仅是在编译器内部生成一个用于模板的数据结构
3.函数模板的隐式推断
函数模板虽然可以手动实例化,但使用麻烦,因此一般都根据参数类型进行隐式推断模板的参数
注意:不能隐式打断的三种情况.
1.函数参数与模板类型参数没有关系
2.不允许隐式类型转换
3.返回值类型不能隐式推断
4.函数模板与默认下次之间有冲突;
5.普通函数与同名的模板函数,构成重载,编译器会优先调用普通函数
如果实现一个与模板函数功能一致的普通函数,那么这就叫做模板函数的特化
注意:一般char*类型都需要特化
三.类模板
1.类模板的定义
template<typename M,typename R,typename A,typename O>
class 类名
{
public:
M val;
Test(A a)
{
O val;
}
R func(void)
{
}
};
2.类模板的使用
类模板的参数不支持隐式推断,必须显式指定类型参数
类名<类型…>对象;
类模板分为两步进行实例化:
编译期:编译器将类模板实例化为类,并生成类对象创建指令
运行期:处理器执行类对象创建指令,将类实例化为对象
类模板也是一种静态多态
类模板中,只有那些被调用的成员函数才实例化,即产生二进制指令(调用谁实例化谁)
3.类模板的静态成员
静态成员需要在类外定义,这一点不改变,但与普通类的定义不同
template<typename …>类型 类名<…>::成员名;
4.递归实例化
类模板的参数可以是任何类型,只要该类型提供类模板所需要的功能
类模板的实例化已经是一个有效的类型了,因此它也可以当作类模板的参数,这种叫递归的实例化
Vector<Vector> //二维数组
Test<Test>
5.类的局部特化
当类的某个成员函数不能通用,需要对特殊类型(char*)实现一个特殊版本,这叫类的局部特化
template<> 类型 返回值类型 类名<类型>::函数名(参数)
{
}
注意:在类外实现
6.全类特化
当需要针对某种类型对类全部实现一个特殊版本,这种叫类的全部特化
template<> 类名<特定类型>
{
…
};
7.类模板的缺省值
类模板的类型参数可以设置默认类型,规则与函数的默认形参基本一致(设置缺省值的类型靠右)
后面的类型参数可以使用前面的类型,但前面不能使用后面的
8.普通数据也可以做为模板参数
template<template T,类型B>
{
int arr[B];
}
给类模板一个数据,在类中就可以像使用宏名一样使用参数
注意:实例化类中提供的数据必须是一个常量