一、什么是模板 模板是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、类模板的默认形参 类模板的参数也可以设置默认参数,用法与函数模板一致(靠右)。
五、STL STL是Stand Template Library缩写,也标准模板库,由惠普实验室提供。
分为以下内容: 容器:常用的一些数据结构,可以存放任何数据类型。 算法:一些常用的算法,如:查找、排序、比较、交换,拷贝。 迭代器:帮助使用容器的工具,使用方式与指针类似。 1、迭代器 实现了*和->运算符的类对象,也支持++/–运算符,可以方便的遍历、访问、操作容器。 正向迭代器:容器类型<类型>::iterator it; 使用++运算符,相当于从头尾遍历容器。 容器的begin成员函数可以获取正向迭代器的起始值。 容器的end成员函数可以获取正向迭代器的末尾(最后一元素的后面)。 常正常迭代器:容器类型<类型>::const_iterator cit; 在正向迭代器的基础上增加的const属性,不能再通过迭代器去修改容器元素的值。 逆向迭代器:容器类型<类型>::reverse_iterator rit; 使用++运算符,相当于从尾到头逆向遍历容器。 常逆向迭代器:容器类型<类型>::const_reverse_iterator crit; 在逆向迭代器的基础上增加的const属性。
2、容器: Vector 支持的运算符:==、!=、<=、>=、<、>、[] 构造函数: vector(); vector( size_type num, const TYPE &val ); vector( const vector &from ); vector( input_iterator start, input_iterator end );
void assign( input_iterator start, input_iterator end ); 将区间[start, end)的元素赋到当前vector void assign( size_type num, const TYPE &val ); 赋num个值为val的元素到vector中
TYPE at( size_type loc ); 相当于[]功能,at() 函数比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素,越界时at会抛异常,而[]可能产生脏数据、段错误等。
iterator erase( iterator loc ); 删除一个元素,之前的迭代器已经补破坏,要重新接收它的返回值。
iterator erase( iterator start, iterator end ); 删除一批元素
size_type size(); 获取当前元素的数量 stack、queue、deque、priority_queue 无法使用迭代器。 deque叫双端队列,两个端口都能进出。 priority_queue优先队列,会根据元素的值自动进行排序,出队列的顺序就是排序后的顺序,存储自定义类型对象时需要实现 < 运算符。 set 集合容器,特点是元素不能重复,并且会自动排序,不支持运算符,只有无参构造,存储自定义类型对象时需要实现 < 运算符。 pair equal_range( const key_type &key ); 返回集合中与给定值相等的上下限的两个迭代器。
iterator lower_bound( const key_type &key ); 返回一个指向大于或者等于key值的第一个元素的迭代器。
iterator upper_bound( const key_type &key ); 在当前集合中返回一个指向大于Key值的元素的迭代器。 multiset 多重集合容器,与set的区别是元素可以重复。 map 叫映射容器,其它编程语言中也叫字典,它的元素由key/value的键值对组成。 pair<key类型,value类型>(k,v) 创建一个键对。
iterator insert( iterator pos, const pair<KEY_TYPE,VALUE_TYPE> &val ); void insert( input_iterator start, input_iterator end ); pair<iterator, bool> insert( const pair<KEY_TYPE,VALUE_TYPE> &val ); 插入元素后会自动排序,key类型要支持<运算符。
iterator find( const KEY_TYPE &key ); 它的查询效率非常高,底层采用红黑树进行存储。
pair类型访问数据: first second multimap: 与映射的区别就中它的key可以重复,查询时返回key相等的所有元素的迭代器。
bitset 它可以把一个无符号长整型的整数转换成二进制,方便的进行位置运算。