DAY1
一、为什么使用模板
- C/C++是一种静态类型语言(预处理->汇编->编译->链接),好处是速度快,缺点是实现通用代码麻烦,例如:实现支持所有类型的快速排序。
- 借助函数重载实现通用代码,好处是实现简单,但代码段会增加。
- 借助宏函数实现通用代码,类型检查不严格
- 借助回调函数实现通用代码,使用麻烦。
- 由于以上原因C++之父在C++实现了模板技术,让C++能够支持泛型编程。
二、函数模板
-
函数模板的定义
template <typename 类型参数1,typename 类型参数2...>
- 返回值
类型参数1 函数名(类型参数2 参数名) { return 返回值; }
template<typename T> T find(T* arr,size_t len) { return val; }
- 可以任何标识符作为类型参数名,但使用’T’时约定俗成的,它代表.
#include<iostream> template<typename T> T Max(T a,T b) { return a > b ? a : b; } int main() { cout << Max() << endl; }
-
函数模板的使用
- C++编译的编译器并不是把模板编译成一个可以处理任何类型的单一实体,而是根据模板使用者的参数,参数不同的函数的实体。
- 根据具体类型代替模板参数生成函数实体过程叫实例化。
- 模板是在使用时才实例化,可以自动实例化,也可以手动实例化(在函数调用时函数名与小括号之间加<类型参数>)。
- 每个函数模板都会进行二次编译,第一次编译在实例化之间,检查模板代码本身是否正确,第二次是实例化过程中,结合所使用类型参数,再次检查模板代码,是否所有的代码都有效。
- 注意:第二次编译才会生成二进制指令,第一次编译仅仅是在编译器内部生成一个用于描述目标的数据结构。
-
函数模板的隐式推断
- 函数模板虽然可以进行手动实例化,但使用麻烦,因此一般都根据参数类型进行隐式推断模板的参数。
- 注意:不能隐式推断的三中情况。
- 函数参数与模板类型参数没有关系
- 不允许隐式转换
- 返回值类型不能隐式推断
-
函数目标与默认形参之间有冲突
-
普通函数与同名的模板函数构成重载的话,编译器会优先调用普通函数。如果实现一个与模板函数功能一致的普通函数,那么这叫做模板函数的特化。
- 注意:一般char*类型都需要特化
三、类模板
-
类模板的定义
template<typename 类型参数,...>
class 类名 { 类型参数 val; };
-
类模板的使用
- 类模板的参数不支持隐式推断,必须显示指定类型参数。类名<类型…>对象;
- 类模板分为两步进行实例化:
- 编译期:编译器将类模板实例化类,并生成类对象创建指令。
- 运行期:处理器执行类对象创建指令,将类实例化为对象。
- 类模板也是一种静态多态。
- 类模板中,只有那些被调用的成员函数才实例化,即产生二进制指令(调用谁实例化谁)。
-
类模板的静态成员
#include<iostream> using namespace std; template <typename A,typename T> class Test { public: static T a; }; template<typename A,typename T> T Test<A,T>::a;
template<typename A,typename T> 类型 类名<A,T>::成员变量;
- 静态成员需要在类外定义,这一点是不改变,但与普通类的定义不同。
-
递归实例化
- 类模板的参数可以是任何类型,只要改类型提供类模板所需要的功能。
- 类模板的实例化已经是一个有效的类型了,因此它也可以当做类模板的参数,这种叫做递归实例化。
Vector<Vector<int>>
二维数组
-
类的局部特化
- 当类的某个成员函数不能通用,需要对特殊类型(char*)实现一个特殊版本,这叫类的局部特化。
template<>返回值类型 类名<类型>::函数名(参数) { }
- 注意:需要在类外实现
- 当类的某个成员函数不能通用,需要对特殊类型(char*)实现一个特殊版本,这叫类的局部特化。
-
全类特化
template<> class 类名<类型> { 类型 成员; 成员函数; }
- 当需要针对某种类型对类全部实现一个特殊版本,这种叫类的全部特化。
-
类模板的缺省值
- 类模板的类型参数可以设置默认值类型,规则与函数的默认形参一致(设置缺省值类型靠右)。后面的类型参数可以使用前面的类型,但前面不能使用后面的。
-
普通数据也可以作为模板参数
template <typename T,类型 B> { int arr[B]; }
- 给类模板提供一个数据,在类中就可以像宏名一样使用参数。
- 注意:实例化类中提供的数据必须是常量。
第二天
一、模板的技巧
- typename可以用class替换(不建议使用)
- 不能直接使用模板父类中的成员,需要
类名<T>::函数名
- 在类模板中可以定义虚函数(可以实现多态),虚函数不能是模板函数
二、STL介绍
- STL:标准模板库,由惠普实验室提供,里面集成了常用的数据结构类模板和算法函数模板等。
- 容器:用来存储个类型数据的数据结构
- 迭代器:类似于专门用来指向容器成员的指针,用来遍历、操作、管理容器中的成员,可以大大提高容器的访问速度。
- 算法:STL实现了常见的排序、查找算法。
- list:双端链表容器
- iterator:用来指向容器中的元素
- begin()获取指向第一个元素的迭代器
- end()获取指向最后一个元素的下一个位置的迭代器
- get_allocator()
- vector:向量容器,俗称数组
- stack、queue:栈和队列
- deque:双端队列,用法和向量差不多,差别在它能从头和尾插入和删除。
- 注意:vector和deque是支持[]运算,因此不需要迭代器,其它容器一律使用迭代器进行遍历。
- set:集合容器,集合中的数据会自动排序,不能重复。
- maps:是一种关联容器,在其它编程语言当中叫字典,C++中叫映射,以key键/value值对的方式进行存储,key的值不能重复
- multimap:多重映射,它与map很像,区别是它的key值可以重复。
- multiset:多重集合,它与set很像,区别是它的值可以重复。
- priotity_queue:优先队列,它会根据元素的比较结果进行排序。
- 总结:
- vector和deque是支持[]运算,因此不需要迭代器,其它容器一律使用迭代器进行遍历
- stack,queue,priority_queue容器没有迭代器
- stack,queue,priority_queue会对元素进行排序,因它存储元素要支持比较运算符
- STL中常用的算法函数
- 使用的时候加头文件:algorithm
- 查找:find,serch
- 复制:copy
- 删除:remove
- 排序:sort
- 比较:equal