一、什么是模板
模板是C++中自动生成代码的技术,例如我们在C++若想实现一个函数的形参可以是多种数据类型就必须使用重载,模板则可以更加简单方便地实现这一点。
二、为什么使用模板
假1如我们想实现一个通用的排序算法
C语言:通过回调函数实现,使用者调用麻烦。
C++语言:函数重载,需要为多种类型实现一个第一版本,还会导致代码段增加。
C/C++语言:借助宏函数实现,类型检查不严格,频繁使用还会增加代码段。
由于以上原因C++之父在C++中实现了模板技术,既能支持多种类也能兼顾严格的类型检查,能让程序员编程专注思考业务逻辑而不用关心数据类型。
解释:
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型(虚拟type),可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
示例:
2、函数模板的使用
C++编译器并不是把函数模板编译成一个可以处理所有类型的实体,而是根据调用者提供的参数,生产不同的函数实体。
根据具体类型带入函数模板产生函数实体的过程叫实例化。
模板是调用时才实体化:
自动实例化:根据调用者提供数据类型自动判断出类型。
手动实例化:func<类型,…>(参数):模板的类型参数与函数的参数没有关系时使用。
ps:模板的参数类型也可以使用默认形参。
3、函数模板的实例化过程
一个模板函数都会经历二次编译,第一次编译是在实例化之前,检查模板代码自身是否正确,第二编译是在实例化时,把调用者提供的类型参数带入模板中再次检查模板代码。
第二次编译才生成二进制的函数指令,第一次编译仅仅是在编译器的内存产生一个用于描述模板的数据结构。
4、函数模板自动实例化的限制
1、函数参数与模板参数没有关系。
2、返回值类型不能隐式推断。
5、函数模板的调用规则
1、函数模板可以像普通函数一样被重载。
2、C++编译器优先考虑普通函数。
3、如果函数模板可以产生一个更好的匹配,那么选择函数模板。
4、可以通过空模板实参列表的语法限制编译器只通过模板匹配。
示例:
5、模板的特化
模板并不适合所有情况,这时可以给特殊类型实现一个正常的函数。
模板函数在实例化之前会先检查有没有正常版本的函数,如果有就不会再继续实例化,因此模板函数与正常函数并不会冲突。
6、模板的工作原理
模板定义并不是真正的定义了一个函数或者类,而是编译器根据程序员缩写的模板和形参来自己写出一个对应版本的定义,这个过程叫做模板实例化。编译器成成的版本通常被称为模板的实例。编译器为程序员生成对应版本的具体过程。类似宏替换。
模板类在没有调用之前是不会生成代码的。
由于编译器并不会直接编译模板本身,所以模板的定义通常放在头文件中。
7、非类型模板参数
顾名思义,模板参数不是一个类型而是一个具体的值——这个值是常量表达式。
当一个模板被实例化时,,非类型参数被一个用户提供的或者编译器推断出的值所代替。正因为模板在编译阶段编译器为我们生成一个对应的版本,所以其值应该能够编译时确定,那么他应该是一个常量或者常量表达式。
template
int str_compare(const char (&str1)[N], const char (&str2)[M])
{
return strcmp(str1,str2);
}
使用方法:
str_compare("hello","nihao");
我们甚至没有用<>来传递模板参数。这是因为编译器在编译阶段已经帮助我们计算好了应该开辟多大空间的数组。我们也可以指定长度。N,M只是隐式的传入进去。
编译器也可以自动帮助我们推断参数时什么类型,从而不用显示的调用模板函数,对于上面的compare函数,我们可以这样调用,前提时保证参数类型相同。
四、类模板
建立一个通用的类,如果希望成员变量的类型是任意的,用类模板实现。
类模板不是一个实际的类型,是虚拟的,和ppt模板一样,实例化的时候才会构建类。
1、类模板的定义格式
2、类模板的使用
模板类的类型参数不能隐式打断,也就是不能自动实例化,必须显式的指定类型参数。
格式:类名<类型> 对象;
类模板的使用分为三个步骤:
1、检查类模板的语法,如果合法则在编译器生成一个类的数据结构。
2、将使用者提供的类型参数代入类模板再次检查语法,如果合法编译器会将类模板实例化,并生成类对象的创建指令。
3、执行类对象的创建指令,创建出类对象。
注意:对于类的成员函数,并不全部实例化,而是调用谁实例化谁(生成二进制指令)。
3、类模板的静态成员
类中的成员要在类中声明,类外定义(具有const属性的外除),类模板的静态成员也一样。
4、类模板的局部特化
当类的成员函数不能通用,需要对特殊类型实现一个特殊版本的成员函数,这叫类的局部特化,必须要在类外实现。
格式:template<> 返回值类型 类名<特殊类型>::函数名(参数列表)
{
}
5、类模板的全局特化
要针对某种特殊类型对类实现一个特殊版本,这叫作类的全局特化。
格式:template <> class 类名<特殊类型>
{
....
};
6、类模板的默认形参
类模板的参数也可以设置默认参数,用法与函数模板一致(靠右)。