模板:实现C++泛型机制,摆脱对类型的依赖
模板的划分:
1、函数模板
2、类模板
一、函数模板
两个数相加:
C语言:
1、以宏函数的方法实现,通过文本替换,预编译阶段没有类型检查和安全检查,不安全
2、用void* 泛型实现,[),如果操作不当,可能发生越界,不安全
C++:
1、函数重载
2、函数模板
template<typename T>//T是模板类型参数,T是任意类型,虚假类型
T Sum(T a, T b)
{
return a+b;
}
int main()
{
Sum<int>(10,20);//<模板类型参数列表>
return 0;
}
上述代码就是我们的函数模板,我们通过调用点Sum<int>(10,20)模板类型参数列表中具体类型去替换虚假类型,也就形成了这样的一个函数:
int Sum(int a,int b)
{
return a+b;
}
上面就是模板函数,就是通过调用点一个具体类型去替换的产物;调用点通过一个具体类型去替换整个虚假类型的过程称为模板的实例化,实例化后的产物是模板函数,函数生成好,我们去编译,编译完成后最终调用的是函数,不是模板。
模板的编译:
1、在定义点只编译模板头部
2、在调用点编译模板实例化后的模板函数
模板的实参推演:通过实参推演出模板的实例化类型
template<typename T>//T是模板类型参数,T是任意类型,虚假类型
T Sum(T a, T b)
{
return a+b;
}
int main()
{
// Sum<int>(10,20);//<模板类型参数列表>
Sum(10,20);
return 0;
}
上述代码在调用点并没有指明我们模板的实例化类型,10,20都是整型,即模板通过实参列表推出用来替换的具体类型是一个整型。
模板的实参推演条件:
- 不能产生二义性(eg:Sum(10,10.1))
- 必须有实参
模板的特例化: (特殊实例化)
char*类型比较大小不能实例化,因为会比较地址,不比较字符串,所以就要使用特例化
template<>
bool Compare(const char* a,const char* b)
{
return strcmp(a,b)>0;
}
int main()
{
Compare("hello","world");
return 0;
}
模板特例化(又称专用化)的划分:
(1)完全特例化(全特化):针对于一个类型,函数模板和类模板都支持全特化
(2)部分特例化(偏特化):针对于部分类型,只有类模板支持偏特化
模板的类型参数:typename T中的T为模板类型参数
- 模板类型参数列表中的类型参数可以通过typename定义或者class定义得到
- 在模板类型参数列表<>里面,typename和class的用法是一样的,但是在外面typename还可以声明一个类型,class可以作为类标识
模板的非类型参数:
template<template T, int len>
- 非类型参数必须是一个常量
- 非类型参数不能是一个浮点型和类类型
模板的重载:
模板版本、模板特例化版本和普通函数版本可以共存,可以重载
调用优先级:普通版本>模板特例化版本>模板版本(必须精确匹配)
//普通函数版本
bool Compare(char* a,char* b)
{
return strcmp(a,b)>0;
}
//函数模板版本
template<templateT>
T Compare(T a,T b)
{
return a>b;
}
//模板特例化版本
template<>
bool Compare(const char* a,const char* b)
{
return strcmp(a,b)>0;
}
int main()
{
Compare("hello","world");//字符串是const char*类型
return 0;
}
考点:
为什么要把模板的整个定义写在.h文件?
//sum.cpp
template<typename T>
T Sum(T a, T b)
{
return a+b;
}
//main.cpp
template<typename T>
T Sum(T,T);
int main()
{
Sum<int>(10,20);
rerturn 0;
}
普通函数把头部(声明)写在.h文件,函数实现(函数定义)在.cpp文件。
模板的处理在编译阶段,编译阶段是以编译单元为单位,也就是先处理一个源文件,再处理另一个源文件,如果模板按照普通函数的方法,先编译sum.cpp,这个编译单元没有调用点,也就是没有模板的实例化,也没有模板函数生成,不会生成任何函数符号;接着编译main.cpp,在调用点实例化后会生成的函数符号,这个函数符号定义在*UND*,接下来进行符号解析解析需要在函数引用的地方找到符号定义的地方,但是在sum.cpp中没有符号生成,符号解析失败,程序没办法运行,所以需要把模板整个放在头文件下,使用的时候包含这个头文件,头文件在预编译阶段处理,会直接把头文件内容展开。
二、类模板
1、类模板:和函数模板差不多
2、模板类:和函数模板差不多
3、类模板的实例化
(1)类模板不允许实参推演,必须明确给出用来实例化的参数类型
(2)类模板实例化是选择性实例化,使用哪个函数类模板实例化哪个函数
4、vector容器
拷贝构造函数的模板退化成构造函数
5、容器适配器
模板类型参数列表<>,列表中可以有:
(1)类型参数 typename|class
(2)非类型参数
(3)模板
6、链表
typename声明模板中的一个类型
考题:typename和class关键字的区别?
1、在模板类型参数列表中class可做类标识,typename可声明模板中的类型
7、类模板的特例化
完全特例化(全特化):模板的逻辑不能满足一个类型(比如char*)
部分特例化(偏特化):模板的逻辑不能满足一部分(比如指针类型)