STL组件概述
一、六大组件
容器、类属算法、迭代器、函数对象、适配器、分配器
二、容器(存储其他对象集合的对象)
1.序列容器(将一组具有相同类型的对象以严格线性的形式组织在一起)
- vector<T>:随机访问编程序列,即:访问复杂度为O(1),对序列末尾的插入和删除均是分摊常量;(数组表示)
- deque<T>:随机访问边长序列,对序列开头和末尾的插入和删除操作均是分摊常量的;(两级数组结构实现)
- list<T>:(双向链表)对变长序列的线性时间访问;对序列中任意位置的插入和删除操作时间复杂度为O(1);(双向链表实现)
注:(1)对于string类和STL序列容器类,其end成员函数返回的是终点之后的迭代器,而不是真正指向序列最后一个元素的迭代器;
(2)STL为所有容器类都定义了==运算符;
2.有序关联容器(有从基于键的集合中快速提取对象的能力,集合的大小可运行时改变)
- set<Key>:支持唯一的键(每个键只出现一次),并可快速检索键;
- multi<Key>:支持可重复的键(同一个键可出现多次),可快速检索键key
- map<Key,T>:键值对,支持唯一的键;可通过键快速检索其对应的类型为T的值;(用find(Key)数据成员实现)
- multimap<Key,T>:键值对,支持可重复的键;可通过键快速检索其对应的类型为T的值;
例,向map中插入元素:
map<string , long> map_test;
map_test["cyj"]=123;
注意:STL容器和其他C++容器类库的重要区别之一:STL容器类并没有为其所含的数据提供过多的操作,而是提供了类属算法;
三、类属算法
1.类属查找算法(适用于任何STL容器)
- find(iterator1 , iterator2, search_content):若找到,返回第一个出现search_content的位置,否则返回容器某位元素后面一个元素的迭代器;
例:
vector<char> vec1 = make<vector<char> >("C++");
find(vec1.begin|(),vec1.end(),'C');
注:与表(list)对应的迭代器不支持+运算符,支持自增预算符++;所有STL容器均支持自增预算符++;
2.类属合并算法(merge,将两个序列内的元素合并到一个序列中)
- 调用形式
merge( first1 , last1 , first2 , last2, result );
(1) 迭代器first1和last1表示一个输入序列的起始和终止位置,元素类型为T;
(2)迭代器first2和last2表示另一个输入序列的起始和终止位置,元素类型为T;
(3)两个输入序列均为升序排列;
(4)result表示合并序列存放的起始位置;注意:结果容器所有元素均升序排列;若前面两个容器中元素均未升序排列,合并后的result容器非升序排列;三个容器不一定是同一类容器,可以属于不同类别,如:分别为list,char[] , deque容器,但元素类型必须为T;
例子:
#include <cassert>
2 #include <iostream>
3 #include <deque>
4 #include <list>
5 #include <algorithm>
6 #include "string.h"
7 #include "stdlib.h"
8 using namespace std;
9
10 template<typename container>
11 container make( const char source[]);
12
13 int main()
14 {
15 char s[]="acd";(升序)
16 int len = strlen(s);
17 list<char> list1 = make< list<char> >("befghijklmnopqrstuvwxy");(升序)
18 deque<char> d(26,'x');
19
20 merge(&s[0],&s[len] , list1.begin() , list1.end(),d.begin());
2 #include <iostream>
3 #include <deque>
4 #include <list>
5 #include <algorithm>
6 #include "string.h"
7 #include "stdlib.h"
8 using namespace std;
9
10 template<typename container>
11 container make( const char source[]);
12
13 int main()
14 {
15 char s[]="acd";(升序)
16 int len = strlen(s);
17 list<char> list1 = make< list<char> >("befghijklmnopqrstuvwxy");(升序)
18 deque<char> d(26,'x');
19
20 merge(&s[0],&s[len] , list1.begin() , list1.end(),d.begin());
assert( d == make< deque<char> >("abcdefghijklmnopqrstuvwxyx"));
23 std::cout<<"result ok"<<std::endl;
24 return 0;
25 }
26
27
28 template<typename container>
29 container make( const char source[])
30 {
31 return container( &source[0] , &source[strlen(source)]);
32 }
23 std::cout<<"result ok"<<std::endl;
24 return 0;
25 }
26
27
28 template<typename container>
29 container make( const char source[])
30 {
31 return container( &source[0] , &source[strlen(source)]);
32 }
四 迭代器(具有和指针类似的特征)
1.迭代器分类
- 输入迭代器:支持!=,*,++,==,但是对于*操作,只能从容器中读取数据,而不能向其中写入数据;
- 输出迭代器:支持!=,*,++,==,但是对于*操作,不能从容器中读取数据,只能向其中写入数据
- 前向迭代器:支持==,!= , *,++操作;
- 双向迭代器:支持==,!= , *,++,--操作;
- 随机访问迭代器:支持==,!= , *,++,--,+=,-=,+,-,<,>,<=,>=操作
- 类属函数accumulate:accumulate(first,last,init),把init和从first到last指向的值进行累加并返回累加得到的和;
注意:accumulate、find、merge等一些用于输入地带器的算法比对迭代器有更高要求的算法(如sort算法要求使用随机访问迭代器)通用性强。
五、函数对象
- 函数对象:一个实体,可以不带参数,也可以带有一个以上的参数,并能够从中获得一个值或改变程序的状态,除了普通函数,另一类函数对象可以是类或结构的对象,且在其定义中重载了函数调用运算符。
例子:
#include <iostream>
2 #include <vector>
3 #include <algorithm>
4 #include <numeric> //for accumulate
5
6 using namespace std;
7
2 #include <vector>
3 #include <algorithm>
4 #include <numeric> //for accumulate
5
6 using namespace std;
7
//函数对象,重载函数调用运算符。
8 class multi{
9 public:
10 //function call overload
11 int operator()(int x , int y ) const
12 {
13 return x*y;
14 }
15 };
16
17 int main()
18 {
19 int x[5]={1,2,3,4,5};
20 vector<int> vec1(&x[0],&x[5]);
8 class multi{
9 public:
10 //function call overload
11 int operator()(int x , int y ) const
12 {
13 return x*y;
14 }
15 };
16
17 int main()
18 {
19 int x[5]={1,2,3,4,5};
20 vector<int> vec1(&x[0],&x[5]);
//1 is inited value
22 int result = accumulate(vec1.begin() , vec1.end() , 1, multi());
23 std::cout<<"result is:"<<result<<std::endl;
24 }
22 int result = accumulate(vec1.begin() , vec1.end() , 1, multi());
23 std::cout<<"result is:"<<result<<std::endl;
24 }
- 在类multi中定义函数调用运算符operator(),就定义了一种可以作为函数参数的对象,可以像使用函数一样使用该对象;multi()使用编译器提供的默认构造函数;该对象没有响应的内存,只有函数定义;
- 函数对象的优势
(1)以类的形式定义函数对象,可以携带更多额外信息,某些类属算法或容器将会用到这类信息;
(2)更加通用、高效
六、适配器(改变其他组件接口的组件)
- 适配器reverse_iterator:将某种类型的迭代器变成一种新的迭代器,但保持其功能不变,将其遍历的方向翻转过来。(使用场景:如用find函数查找某个元素在容器中最后出现的位置;浮点数累加,若元素升序排列,舍入误差通常会小一些,否则,以降序顺序排列时,与累加和相比,非常小的数值对于累加和几乎不起任何作用,因此可用reverse_iterator和accumulate实现降序序列浮点数累加,结果精确些)
- 注:STL每种容器都定义了reverse_iterator,以及返回这种类型迭代器的rbegin和rend成员函数
- 栈适配器:将序列容器变换到先进后出的栈接口中;
- 队列适配器:将序列容器变换到先入先出队列中;
- 优先级队列:将序列容器变换到优先级队列中,由一个比较参数来控制元素访问顺序。
- 函数适配器:
(1)取反器(negator):对判定函数对象(返回值为bool类型)的结果进行取反;
(2)绑定器(binder):将二元函数的一个参数与某个特定的值绑定,从而将二元函数编程一元函数。
(3)函数指针适配器:从函数指针得到函数对象。
七、分配器(封装程序所用的内存分配模式信息)
注:分配器类可以封装许多信息:指针、常量指针、引用、常量引用、对象大小、不同类型指针之间的差别、分配函数与释放函数以及其他一些函数信息。分配器上所有操作都具有分摊常量的运行时间