C++STL(标准模板库)
1.STL简介
STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。
STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但这种分离确实使得STL变得非常通用。例如,由于STL的sort()函数是完全通用的,你可以用它来操作几乎任何数据集合,包括链表,容器和数组;
STL另一个重要特性是它不是面向对象的。为了具有足够通用性,STL主要依赖于模板而不是封装,继承和虚函数(多态性)——OOP的三个要素。你在STL中找不到任何明显的类继承关系。这好像是一种倒退,但这正好是使得STL的组件具有广泛通用性的底层特征。另外,由于STL是基于模板,内联函数的使用使得生成的代码短小高效;
从逻辑层次来看,在STL中体现了泛型化程序设计的思想,引入了诸多新的名词,比如像需求(requirements),概念(concept),模型(model),容器(container),算法(algorithmn),迭代子(iterator)等。与OOP(object-oriented programming)中的多态(polymorphism)一样,泛型也是一种软件的复用技术;
从实现层次看,整个STL是以一种类型参数化的方式实现的,这种方式基于一个在早先C++标准中没有出现的语言特性–模板(template)。
2.六大组件
- 容器(Container),是一种数据结构,如list,vector,和deques ,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;
- 迭代器(Iterator),提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象;
- 算法(Algorithm),是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用;
- 仿函数(Functor)
- 适配器(Adaptor)
- 分配器(allocator)
2.1 容器
参考文档
2.1.1 顺序容器(第一类容器)
2.1.1.1 矢量:vector
表示可以改变大小的数组的序列容器。就像数组一样,向量对它们的元素使用连续的存储位置,这意味着它们的元素也可以使用指向其元素的常规指针上的偏移量来访问,并且效率与数组相同。但是与数组不同,它们的大小可以动态更改,其存储由容器自动处理。
主要使用的成员函数:
Iterators(迭代器):
begin() 返回迭代器到开始
end() 返回迭代器到结束
容量:
size() 返回大小
empty() 测试矢量是否为空
reverse() 矢量翻转
元素访问:
operator[] 访问元素
at() 访问元素
front() 访问第一个元素
back() 访问最后一个元素
修改操作:
push_back() 在末尾添加元素
pop_back() 删除最后一个元素
insert() 插入元素
erase() 擦除元素[可以清除一段,也可以值清除某一点]
swap() 交换内容
clear() 清除内容
2.1.1.2 双端队列:deque
双端队列是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。
迭代:
begin() 返回迭代器到开始
end() 返回迭代器到结束
容量:
size() 返回大小
empty() 检测是否为空
访问元素:
operator[] 访问元素
at() 访问元素
front() 访问第一个元素
back() 访问最后一个元素
修改元素:
push_back() 在末尾插入元素
push_front() 在首部插入元素
pop_back() 删掉末尾的元素
pop_front() 删掉首部的元素
insert() 插入元素
erase() 擦除元素
swap() 交换
clean() 清除
2.1.1.3 列表:list
列表是序列容器,允许在序列中的任何位置进行恒定时间的插入和擦除操作,并在两个方向上进行迭代。
迭代:
begin() 返回迭代器到开始
end() 返回迭代器到末尾
容量:
empty() 检测是否为空
size() 返回大小
max_size() 返回最大大小
元素访问:
front() 访问首部元素
back() 访问尾部元素
修改:
assign() 初始化
emplace_front() 构造和在首部插入元素C++11引入
push_front() 在首部插入元素
pop_front() 删掉首部元素
emplace_back() 构造和在尾部插入元素C++11引入
push_back() 在末尾插入元素
pop_back() 在尾部插入元素
emplace() 构造并自选位置插入C++11引入
insert() 在指定位置的元素之前插入新元素来扩展容器。
erase() 擦除一个或一段
swap() 交换
clear() 清除
操作:
splice() 将元素从一个列表转移到另一个列表
remove() 删掉具有特定值的某元素
remove_if() 删除满足条件的元素
unique() 去重
merge() 合并列表
reverse() 倒置
2.1.2 关联容器(第一类容器)
2.1.2.1 集合:set
集合是按照特定顺序存储唯一元素的容器。
迭代器:
begin() 返回迭代器到首部
end() 返回迭代器到尾部
容量:
empty() 检测集合是否为空
size() 返回容器大小
max_size() 返回最大的容器大小
修改:
insert() 插入元素
erase() 擦除元素
swap() 交换元素
clear() 清除
观察员:
key_comp() 返回比较对象,默认less,所以而这比较是小于的话返回true
value_comp() 返回比较对象,默认less,所以而这比较是小于的话返回true
操作:
find() 获取元素的迭代器
count() 计算具有特定值的元素
lower_bound() 返回迭代器到上限
upper_bound() 返回迭代器到下线
equal_range() 获取相同元素的范围,是两个迭代器
2.1.2.2 映射:map
映射是关联容器,用于存储按特定顺序由键值和映射值的组合形成的元素。
迭代器:
begin() 返回迭代器到首部
end() 返回迭代器到尾部
容量:
empty() 检测是否为空
size() 返回容器大小
max_size() 返回最大大小
元素访问:
operator[] 访问元素
at() 访问元素 c++11引入
修改:
insert() 插入元素
erase() 擦除元素
swap() 交换元素
clear() 清除元素
emplace() 构造并插入元素
emplace_hint() 选位置插入元素
观察员:
key_comp()
value_comp()
操作:
find()
count()
lower_bound()
upper_bound()
equal_bound()
2.1.2.3 多重集合:multiset
与set区别:
multiset:
- 可以插入完全相同的两条记录。
- 会提高数据插入的速度。
set:
- 不可以插入完全相同的两条记录
- 保证记录的唯一性
- 由于需要查重处理,会降低数据插入的速度
- 可以作为一种去重的方法
迭代器:
begin()
end()
容量:
empty()
size()
max_size()
修改:
insert()
erase()
swap()
clear()
观察者:
find()
count()
lower_bound()
upper_bound()
equal_range()
2.1.2.4 多重映射:multimap
multimap中的key可以重复,没有重载operator[ ]功能,对于重复的元素,查找的时候也是返回中序遍历的第一个元素。
迭代器:
begin()
end()
容器:
empty()
size()
max_size()
修改:
insert()
erase()
swap()
clear()
emplace() C++11引入
emplace_hint() C++11引入
观察者:
key_comp()
value_comp()
操作:
find()
count()
lower_bound()
upper_bound()
equal_range()
2.1.2.5 无序映射:unordered_map
和hash_map、map的区别:
1.和map的区别
内部实现机理不同,map的内部结构是R-B-tree来实现的,所以保证了一个稳定的动态操作时间,查询、插入、删除都是O(logn),最坏和平均都是。
hash_map是哈希表,哈希表的查询时间虽然是O(1),但是并不是unordered_map查询时间一定比map短,因为实际情况中还要考虑到数据量,而且unordered_map的hash函数的构造速度也没那么快,所以不能一概而论,应该具体情况具体分析。
2.和hash_map的区别
这两个的内部结构都是采用哈希表来实现。
unordered_map在C++11的时候被引入标准库了,而hash_map没有,所以建议还是使用unordered_map比较好。
2.1.2.6 无序集:unordered_set
在以下情况下使用unordered_set:
- 我们需要保留一组不同的元素,并且不需要排序。
- 我们需要单元素访问,即无遍历。
何时使用set:
- 我们需要有序的数据。
- 我们将不得不打印/访问数据(按排序顺序)。
- 我们需要元素的前任/后继。
- 由于set是有序的,因此我们可以在set元素上使用binary_search(),lower_bound()和upper_bound()之类的函数。这些函数不能在unordered_set()上使用。
2.2 Iterator(STL迭代器)
Iterator(迭代器)模式又称Cursor(游标)模式。用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。或者这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。
类似智能指针,重载了很多指针的成员函数,是指针的提升。
2.2.1 迭代器的作用
能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型:iterator、const_iterator、reverse_iterator和const_reverse_iterator.
2.3 算法
我愿称之为神!!!!
2.3.1 概述
STL算法部分主要由头文件
, , 组成。要使用 STL中的算法函数必须包含头文件 ,对于数值算法须包含 , 中则定义了一些模板类,用来声明函数对象。
2.3.2 常用算法①:查找算法
查找算法共13个,包含在
头文件中,用来提供元素排序策略。
这里只列出一部分算法:
- count:利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。
- count_if:利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。
- binary_search:在有序序列中查找value,找到返回true。重载的版本实用指定的比较函数对象或函数指针来判断相等。
- equal_range:功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。
- find:利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回指向该元素的Iterator。
- search:给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作。
- search_n:在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作。
2.3.3 常用算法②:排序和通用算法
排序算法共14个,包含在
头文件中,用来判断容器中是否包含某个值。
- merge:合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较。
- random_shuffle:对指定范围内的元素随机调整次序。重载版本输入一个随机数产生操作。
- nth_element:将范围内的序列重新排序,使所有小于第n个元素的元素都出现在它前面,而大于它的都出现在后面。重载版本使用自定义的比较操作。
- reverse:将指定范围内元素重新反序排序。
- sort:以升序重新排列指定范围内的元素。重载版本使用自定义的比较操作。
- stable_sort:与sort类似,不过保留相等元素之间的顺序关系。
2.3.4 常用算法③:删除和替换算法
删除和替换算法共15个,包含在
头文件中。
- copy:复制序列。
- copy_backward:与copy相同,不过元素是以相反顺序被拷贝。
- remove:删除指定范围内所有等于指定元素的元素。注意,该函数不是真正删除函数。内置函数不适合使用remove和remove_if函数。
- remove_copy:将所有不匹配元素复制到一个制定容器,返回OutputIterator指向被拷贝的末元素的下一个位置。
- remove_if:删除指定范围内输入操作结果为true的所有元素。
- remove_copy_if:将所有不匹配元素拷贝到一个指定容器。
- replace:将指定范围内所有等于vold的元素都用vnew代替。
- replace_copy:与replace类似,不过将结果写入另一个容器。
- replace_if:将指定范围内所有操作结果为true的元素用新值代替。
- replace_copy_if:与replace_if,不过将结果写入另一个容器。
- swap:交换存储在两个对象中的值。
- swap_range: 将指定范围内的元素与另一个序列元素值进行交换。
- unique: 清除序列中重复元素,和remove类似,它也不能真正删除元素。重载版本使用自定义比较操作。
- unique_copy:与unique类似,不过把结果输出到另一个容器。
2.3.5 常用算法④:排列组合算法
排列组合算法共2个,包含在
头文件中,用来提供计算给定集合按一定顺序的所有可能排列组合。
- next_permutation:取出当前范围内的排列,并重新排序为下一个字典序排列。重载版本使用自定义的比较操作。
- prev_permutation:取出指定范围内的序列并将它重新排序为上一个字典序排列。如果不存在上一个序列则返回false。重载版本使用自定义的比较操作。
2.3.6 常用算法⑤:数值算法
数值算法共4个,包含在
头文件中。
- accumulate:iterator对标识的序列段元素之和,加到一个由val指定的初始值上。重载版本不再做加法,而是传进来的二元操作符被应用到元素上。
- partial_sum:创建一个新序列,其中每个元素值代表指定范围内该位置前所有元素之和。重载版本使用自定义操作代替加法。
- inner_product:对两个序列做内积(对应元素相乘,再求和)并将内积加到一个输入的初始值上。重载版本使用用户定义的操作。
- adjacent_difference:创建一个新序列,新序列中每个新值代表当前元素与上一个元素的差。重载版本用指定二元操作计算相邻元素的差。
2.3.7 常用算法⑥:生成和异变算法
生成和异变算法共6个,包含在
头文件中。
- fill:将输入值赋给标志范围内的所有元素。
- fill_n:将输入值赋给first到first+n范围内的所有元素。
- for_each:用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。该函数不得修改序列中的元素。
- generate:连续调用输入的函数来填充指定的范围。
- generate_n:与generate函数类似,填充从指定iterator开始的n个元素。
- transform:将输入的操作作用与指定范围内的每个元素,并产生一个新的序列。重载版本将操作作用在一对元素上,另外一个元素来自输入的另外一个序列。结果输出到指定容器。
2.3.8 常用算法⑦:关系算法
关系算法共8个,包含在
头文件中。
- equal:如果两个序列在标志范围内元素都相等,返回true。重载版本使用输入的操作符代替默认的等于操作符。
- includes:判断第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的<操作符,成功返回true。重载版本使用用户输入的函数。
- max: 返回两个元素中较大一个。重载版本使用自定义比较操作。
- min:返回两个元素中较小一个。重载版本使用自定义比较操作。
- max_element:返回一个ForwardIterator,指出序列中最大的元素。重载版本使用自定义比较操作。
- min_element:返回一个ForwardIterator,指出序列中最小的元素。重载版本使用自定义比较操作。
- mismatch: 并行比较两个序列,指出第一个不匹配的位置,返回一对iterator,标志第一个不匹配元素位置。如果都匹配,返回每个容器的last。重载版本使用自定义的比较操作。
2.3.9 常用算法⑧:集合算法
集合算法共4个,包含在
头文件中。
- set_union:构造一个有序序列,包含两个序列中所有的不重复元素。重载版本使用自定义的比较操作。
- set_intersection:构造一个有序序列,其中元素在两个序列中都存在。重载版本使用自定义的比较操作。
- set_difference: 构造一个有序序列,该序列仅保留第一个序列中存在的而第二个中不存在的元素。重载版本使用自定义的比较操作。
- set_symmetric_difference:构造一个有序序列,该序列取两个序列的对称差集(并集-交集)。
2.3.10 常用算法⑨:堆算法
集合算法共4个,包含在
头文件中。
- make_heap:把指定范围内的元素生成一个堆。重载版本使用自定义比较操作。
- pop_heap:并不真正把最大元素从堆中弹出,而是重新排序堆。它把first和last-1交换,然后重新生成一个堆。可使用容器的back来访问被”弹出”的元素或者使用pop_back进行真正的删除。重载版本使用自定义的比较操作。
- push_heap:假设first到last-1是一个有效堆,要被加入到堆的元素存放在位置last-1,重新生成堆。在指向该函数前,必须先把元素插入容器后。重载版本使用指定的比较操作。
2.4 仿函数
2.4.1 概述
仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
除了使用STL内建的仿函数,还可使用自定义的仿函数.
2.4.2 STL中的仿函数
(1)算术类仿函数
- 加:plus
- 减:minus
- 乘:multiplies
- 除:divides
- 模取:modulus
- 否定:negate
(2)关系运算类仿函数
- 等于:equal_to
- 不等于:not_equal_to
- 大于:greater
- 大于等于:greater_equal
- 小于:less
- 小于等于:less_equal
(3)逻辑运算仿函数
- 逻辑与:logical_and
- 逻辑或:logical_or
- 逻辑否:logical_no
2.5 容器适配器(adapters)
一种用来修饰容器(containers)或仿函式(functors)或迭代器(iterators)接口的东西。
栈,队列,优先队列。
2.5.1 栈:stack
LIFO,堆栈是一种容器适配器,专门设计用于在LIFO上下文(后进先出)中操作,在LIFO上下文中,仅从容器的一端插入和提取元素。
成员函数:
empty()
size()
top()
push()
emplace() C++11引入
pop()
swap()
非成员函数重载:
swap(stack) C++11引入
2.5.2 队列:queue
成员函数:
empty()
size()
front()
back()
push()
emplace()
pop()
swap()
非成员函数重载:
swap(queue)
2.5.3 优先队列:priority_queue
优先级队列是一种容器适配器,经过专门设计,以使其按照某些严格的弱排序标准,其第一个元素始终是其中包含的最大元素。
此上下文类似于堆,可以在任何时候插入元素,并且只能检索最大堆元素(优先级队列顶部的元素)
成员函数:
empty()
size()
top()
push()
emplace() C++11
pop()
swap() C++11
非成员函数重载:
swap(queue)
2.6 配置器(allocators)
负责空间配置与管理。从实作的角度看, 配置器是一个实现了动态空间配置、空间管理、空间释放的 class template。
- 本文作者:lybbor
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!