一、什么是模板
模板是C++中自动生成代码的技术。
二、为什么使用模板
问题:实现一个通用的排序算法。
C语言:通过回调函数实现,使用者调用麻烦。
C++语言:函数重载,需要为多种类型实现一个第一版本,还会导致代码段增加。
C/C++语言:借助宏函数实现,类型检查不严格,频繁使用还会增加代码段。
由于以上原因C++之父在C++中实现了模板技术,既能技术多种类也能兼顾严格的类型检查,能让程序员编程专注思考业务逻辑而不用关系数据类型。
三、函数模板
1、函数模板的定义
template <typename T1,typename T2,…>
T2 函数名(T1 a1,T2,a2)
{
T1 v1;
T2 v2 = x;
return v2;
}
可以使用任何标识符作来类型参数,但使用T是俗成约定的。
typedef 也可以换成 class 它们没有任何区别。
2、函数模板的使用
C++编译器并不是把函数模板编译成一个可以处理所有类型的实体,而是根据调用者提供的参数,生产不同的函数实体。
根据具体类型带入函数模板产生函数实体的过程叫实例化。
模板是调用时才实体化:
自动实例化:根据调用者提供数据类型自动判断出类型。
手动实例化:func<类型,…>(参数)
模板的类型参数,与函数的参数没有关系时使用。
模板的参数类型也可以使用默认形参。
3、模板函数的实例化过程
第一个模板函数都会经历二次编译,第一次编译是在实例化之前,检查模板代码自身是否正确,第二编译是在实例化时,把调用者提供的类型参数带入模板中再次检查模板代码。
第二次编译才生成二进制的函数指令,第一次编译仅仅是在编译器的内存产生一个用于描述模板的数据结构。
4、模板函数自动实例化的限制
1、函数参数与模板参数没有关系。
2、返回值类型不能隐式推断。
5、模板的特化
模板并不适合所有情况,这时可以给特殊类型实现一个正常的函数。
模板函数在实例化之前会先检查有没有正常版本的函数,如果有就不会再继续实例化,因此模板函数与正常函数并不会冲突。
四、类模板
1、类模板的定义格式
template <typename M,typename R,typename A,typename O>
class Test
{
private:
M val;
public:
R func(A a)
{
O a;
}
};
2、类模板的使用
模板类的类型参数不能隐式打断,也就是不能自动实例化,必须显式的指定类型参数。
类名<类型> 对象;
类模板的使用分为三个步骤:
1、检查类模板的语法,如果合法则在编译器生成一个类的数据结构。
2、将使用者提供的类型参数代入类模板再次检查语法,如果合法编译器会将类模板实例化,并生成类对象的创建指令。
3、执行类对象的创建指令,创建出类对象。
注意:对于类的成员函数,并不全部实例化,而是调用谁实例化谁(生成二进制指令)。
3、类模板的静态成员
类中的成员要在类中声明,类外定义(具有const属性的外除),类模板的静态成员也一样。
template
class Test
{
static int val1;
static T val2;
public:
};
templateint Test::val1 = value;
templateT Test:: val2 = value;
4、递归实例化
模板的参数可以是任何类型,只要该类提供了模板代码中所需要的功能。
类模板实例化后已经是一种类型了,所以它也只可以当模板的参数,这种实例化叫递归实例化。
5、类模板的局部特化
当类的成员函数不能通用,需要对特殊类型实现一个特殊版本的成员函数,这叫类的局部特化,必须要在类外实现。
template<> 返回值类型 类名<特殊类型>::函数名(参数列表)
{
}
6、类模板的全局特化
要针对某种特殊类型对类实现一个特殊版本,这叫作类的全局特化。
template <> class 类名<特殊类型>
{
…
};
7、类模板的默认形参
类模板的参数也可以设置默认参数,用法与函数模板一致(靠右)。