C++ STL 系列——基本概念
一、STL 基本组成
通常认为,STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成,其中后面 4 部分是为前 2 部分服务的。
STL 的组成 | 含义 |
---|---|
容器 | 一些封装数据结构的模板类,例如 vector 向量容器、list 列表容器等。 |
算法 | STL 提供了非常多(大约 100 个)的数据结构算法,它们都被设计成一个个的模板函数,这些算法在 std 命名空间中定义,其中大部 |
分算法都包含在头文件 |
|
迭代器 | 在 C++ STL 中,对容器中数据的读和写,是通过迭代器完成的,扮演着容器和算法之间的胶合剂。 |
函数对象 | 如果一个类将 () 运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象(又称仿函数)。 |
适配器 | 可以使一个类的接口(模板的参数)适配成用户指定的形式,从而让原本不能在一起工作的两个类工作在一起。值得一提的是,容器、迭代器和函数都有适配器。 |
内存分配器 | 为容器类模板提供自定义的内存申请和释放功能,由于往往只有高级用户才有改变内存分配策略的需求,因此内存分配器对于一般用户来说,并不常用。 |
在 C++ 的标准中,STL 被组织为13个头文件:
<iterator>、<functional>、<vector>、<deque>
<list>、<queue>、<stack>、<set>
<map>、<algorithm>、<numeric>、<memory>
<utility>
二、什么是容器
简单的理解容器,它就是一些模板类的集合,但和普通模板类不同的是,容器中封装的是组织数据的方法(也就是数据结构)。STL 提
供有 3 类标准容器,分别是序列容器、排序容器和哈希容器,其中后两类容器有时也统称为关联容器。
容器种类 | 功能 |
---|---|
序列容器 | 主要包括 vector 向量容器、list 列表容器以及 deque 双端队列容器。之所以被称为序列容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置,元素就会位于什么位置。 |
排序容器 | 包括 set 集合容器、multiset 多重集合容器、map 映射容器以及 multimap 多重映射容器。排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置。所以关联容器在查找时具有非常好的性能。 |
哈希容器 | C++ 11 新加入 4 种关联式容器,分别是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函数确定。 |
三、STL 迭代器
迭代器可以是需要的任意类型,通过迭代器可以指向容器中的某个元素,除了具有对容器进行遍历读写数据的能力之外,还能对外隐藏容器的内部差异。
迭代器类别
常用的迭代器按功能强弱分为输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。
- 输入/输出迭代器是把输入/输出流作为操作对象;
- 前向(正向)迭代器支持往后移动,还可以被复制或赋值。此外两个正向迭代器可以互相赋值;
- 双向迭代器既能向前也能向后移动,具有正向迭代器的全部功能;
- 随机访问迭代器具有双向迭代器的全部功能,此外还能以当前位置为基准随机访问第 i 个元素或返回第 i 个元素的迭代器。此外两个随机访问迭代器还可以用 > 、< 运算符进行比较
不同容器的迭代器
容器 | 对应的迭代器类型 |
---|---|
array | 随机访问迭代器 |
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set/multiset | 双向迭代器 |
map/multimap | 双向迭代器 |
forward_list | 前向迭代器 |
unordered_map/unordered_multimap | 前向迭代器 |
unordered/unordered_multiset | 前向迭代器 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
迭代器的定义方式
尽管不同容器对应着不同类别迭代器,但这些迭代器有着较为统一的定义方式,具体分为 4 种:
迭代器定义方式 | 具体格式 |
---|---|
正向迭代器 | 容器类名::iterator 迭代器名 |
常量正向迭代器 | 容器类名::const_iterator 迭代器名 |
反向迭代器 | 容器类名::reverse_iterator 迭代器名 |
常量反向迭代器 | 容器类名::const_reverse_iterator 迭代器名 |
- 对正向迭代器进行 ++ 操作时,迭代器会指向容器中的后一个元素;
- 对反向迭代器进行 ++ 操作时,迭代器会指向容器中的前一个元素。
- 以上 4 种定义迭代器的方式,不是每个容器都适用。
#include <vector>
int main(){
vector<int> v{1,2,3,4,5,6,7,8,9};
vector<int>::iterator i;
for( i=v.begin(); i!= v.end(); ++i)
cout << *i << " ";
for(i=v.begin(); i<v.end();>){
cout << *i << " ";
i += 2; // 随机访问迭代器,支持 <,[], += 等操作
}
}
再举一个例子, list 容器的迭代器是双向迭代器,那么它能进行的操作和 vector 就会不一样。
list<int> v;
list<int>::const_iterator i;
for( i = v.begin(); i!= v.end(); ++i)
cout << *i;
// 错误,双向迭代器不支持 <
for( i = v.begin();i<v.end(); ++i )
cout << *i;
// 错误,双向迭代器不支持下标随机访问
for(int i = 0; i <v.size; i++)
cout << v[i];