C++容器的特性与适用场景, PPT
容器类别
首先放上一张来自《C++标准库》中的图片。
序列式容器(Sequence container)
这是一种有序(ordered)集合,其内每个元素均有确凿的位置—-取决于插入时机和地点,与元素值无关。如果你以追加方式对一个集和置入6个元素,他们的排列次序将和置入次序一致。STL提供了5个定义好的序列式容器:array、vector、deque、list和forward_list。
关联式容器(Associative container)
这是一种已排序(sorted)集合,元素位置取决于其value(或key—-如果元素是个key/value pair)和给定的某个排序准则。如果将六个元素置入这样的集合中,他们的值将决定他们的次序,和插入次序无关。STL提供了4个关联式容器:set、multiset、map和multimap。
无序容器(Unordered (associative) container)
这是一种无序集合(unordered collection), 其内每个元素的每个位置无关紧要,唯一重要的是某特定元素是否位于此集合内。元素值或其安插顺序,都不影响元素的位置,而且元素的位置有可能在容器生命周期中被改变。如果你放6个元素到这种集合内,它们的次序不明确,并且可能随时间而改变。STL内含4个预定义的无序容器:unordered_set、unordered_multiset、unordered_map和unordered_multimap。
Sequence容器通常被实现为array或linked list
Associative容器通常被实现为binary tree
Unordered容器通常被实现为hash table
各种容器使用时机
默认情况下应该使用
std::vector
。std::vector
的内部构造最简单,并允许随机访问,所以数据的访问十分方便灵活,数据的处理也够快。如果经常要在序列头部和尾部安插和一处元素,应该采用
std::deque
。如果你希望元素被移除时,容器能够自动缩减内部用量,那么也该使用std::deque
。此外,由于std::vector
通常采用一个内存区块来存放元素,而std::deque
采用多个区块,所以后者可内含更多元素。如果需要经常在容器中段执行元素安插、移除和移动,可考虑使用
std::list
。std::list
提供特殊的成员函数,可在常量时间内将元素从A容器转移到B容器。但由于std::list
不支持随机访问,所以如果只知道list的头部却要造访list的中端元素,效能会大打折扣。和所有“以节点为基础”的容器相似,只要元素仍是容器的一部分,list就不会令指向那些元素的迭代器失效。std::vector
则不然,一旦超过其容量,它的所有iterator
、pointer
和reference
失效。至于std::deque
,当它的大小改变,所有iterator
、pointer
和reference
都会失效。如果你要的容器对异常处理使得“每次操作若不成功便无任何作用”,那么应该选用
std::list
(但是不调用其assignment操作符和sort(), 而且如果元素比较过程中会抛出异常,就不要调用merge()、remove()、remove_if()和unique(),或选用associative/unordered容器(但不调用多元素安插动作,而且如果比较准则的复制/赋值动作可能抛出异常,就不要调用swap()或erase()))。如果你经常需要根据某个准则查找元素,应当使用“依据该准则进行hash”的
std::unordered_set
或std::multiset
。然而,hash容器内是无序的,所以如果你必须以来元素的次序(order),应该使用std::set
或std::multiset
,他们根据查找准则对元素排序。如果想处理key/value pair,请采用
unordered_map
或std::unordered_multimap
。如果元素次序很重要,可采用std::map
或std::multimap
。如果需要关联式数组(associative array), 应采用unordered map。如果元素次序很重要,可采用
std::map
。如果需要字典结构,应采用
unordered std::multimap
。如果元素次序很重要,可采用std::multimap
。
Array | Vector | Dequeue | List | Forward List | 关联容器 | 无序容器 | |
---|---|---|---|---|---|---|---|
可用标准 | TR1 | C++98 | C++98 | C++11 | C++98 | C++98 | TR1 |
数据结构 | 静态数组 | 动态数组 | 数组的数组 | 双向链表 | 单向链表 | 二叉树 | 哈希表 |
元素类型 | value | value | value | value | value | set: value map: key/value |
set: value map: key/value |
是否允许重复 | 是 | 是 | 是 | 是 | 是 | 只有 multiset 和 multimap 允许重复 |
只有 multiset 和 multimap 允许重复 |
迭代器类型 | 随机访问 | 随机访问 | 随机访问 | 双向迭代器 | 单向迭代器 | 双向迭代器 | 单向迭代器 |
增长/缩小方式 | 不会增长/缩小 | 在一端末尾增长 | 在两端末尾增长 | 到处增长 | 到处增长 | 到处增长 | 到处增长 |
是否可以随机访问 | 是 | 是 | 是 | 否 | 否 | 否 | 差不多 |
查找元素 | 慢 | 慢 | 慢 | 非常慢 | 非常慢 | 快 | 非常快 |
添加和删除操作是否会使迭代器无效 | - | 在重新申请内存时无效 | 总是无效 | 从不 | 从不 | 从不 | 在重新哈希时 |
添加和删除操作是否会使引用或指针无效 | - | 在重新申请内存时无效 | 总是无效 | 从不 | 从不 | 从不 | 从不 |
是否允许保留内存 | - | 是 | 否 | - | - | - | 是 |
移除元素时释放内存 | - | 只有在shrink_to_fit() 时释放内存 |
有时 | 总是 | 总是 | 总是 | 又是 |
事务安全(成功或没有影响) | No | 在尾部push /pop 事务安全 |
在头部和尾部push /pop 安全 |
所有的插入和擦除 | 所有的插入和擦除 | 假如比较函数没有出现异常,那么单个元素的插入和所有擦除操作都是事务安全的 | 假如比较函数和哈希函数没有出现异常,那么单个元素的插入和所有擦除操作都是事务安全的 |
容器的共同能力
- 所有容器提供的都是 “value 语义” 而非 “reference 语义”。容器进行元素的安插动作是,内部实施的是
copy
和/或move
动作, 而不是管理元素的reference
。 如果不想要复制,那么只能使用std::move
或 保存元素指针(不能使用引用来规避复制)。 - 元素在容器内有其特定顺序。每一种容器会提供若干返回迭代器的操作函数,这些迭代器可以用来遍历各个元素。如果你在元素之间迭代多次,你会获得相同的次序(不调用增删函数)
- 一般而言,各项操作并非绝对安全,也就是说他们不会检查每一个可能发生的错误。调用者必须确保传给操作函数的实参符合条件。
- 都提供如下成员函数
函数 | 注解 |
---|---|
default construct | |
copy construct | |
destructor | |
begin() | |
end() | |
cbegin() | after C++11 |
cend() | after C++11 |
clear() | |
swap() | std::array: O(n), 其他容器: O(1) |
empty() | empty() 的实现可能比 size() == 0 更有效率,尽可能使用该函数 |
size() | |
max_size() | |
empty() | |
operator== | |
operator!= | |
operator< | 除了无序容器 |
operator<= | 除了无序容器 |
operator> | 除了无序容器 |
operator>= | 除了无序容器 |
容器遍历方式
1 | /// After C++11 |
size() == 0 与 empty()
在 C++11
之前 std::list::empty()
函数的时间复杂度可能是 O(n)
也可能是 O(1)
.
std::array
C++11
- 随机访问
- 固定大小, 编译期确定
- 大小可为零
应用场景
1 | /// 作为返回值返回 |
std::vector
迭代器示意
at() 与 operator[]
at()
成员函数提供边界检查,超出边界时会抛出异常 std::out_of_range
size() 与 capacity()
size() 查看当前有几个元素
capacity() 查看预分配几个元素的空间
resize() 与 reserve()
resize(count)
重设容器大小以容纳 count 个元素。
若当前大小大于 count ,则减小容器为其首 count 个元素。
若当前大小小于 count ,
- 则后附额外的默认插入的元素
- 则后附额外的 value 的副本
reserve(new_cap)
增加 vector 的容量到大于或等于 new_cap 的值。若 new_cap 大于当前的 capacity() ,则分配新存储,否则该方法不做任何事。
reserve() 不更改 vector 的 size 。
若 new_cap 大于 capacity() ,则所有迭代器,包含尾后迭代器和所有到元素的引用都被非法化。否则,没有迭代器或引用被非法化。
shrink_to_fit() (C++11)
请求移除未使用的容量。
它是减少 capacity() 到 size()非强制性请求。请求是否达成依赖于实现。
若发生重分配,则所有迭代器,包含尾后迭代器,和所有到元素的引用都被非法化。若不发生重分配,则没有迭代器或引用被非法化。
C++11 之前可以使用如下方式,缩减空间
1 | template<typename T> |
push_back() 与 push_front()
insert()
erase()
1 | std::vector<int> vec{1,2,3}; |
data()
返回指向作为元素存储工作的底层数组的指针。指针满足范围 [data(); data() + size()) 始终是合法范围,即使容器为空(该情况下 data() 不可解引用)。
拷贝 vector
中的数据到缓冲区
1 | unsigned char auc[] = {1,2,3,4,5,6}; |
std::vector < bool >
该容器不是 布尔类型的 std::vector
, 而是有单独的实现,不应看作普通的 std::vector
使用。
异常处理
- 如果
push_back()
安插元素时发生异常,函数将不会产生效用。 - 如果元素的
copy/move
操作(包括构造函数和赋值运算符)不抛出异常,这意味着insert()
、emplace()
、emplace_back()
、push_back()
要么成功,要么什么也不发生。 pop_back()
不会抛出任何异常- 如果元素的
copy/move
的操作(包括构造函数和赋值运算符)不抛出异常,erase()
就不抛出异常 swap()
和clear()
不抛出异常- 如果元素的
copy/move
操作(包括构造函数和赋值运算符)不抛出异常, 那么所有操作不是成功就是不产生效用。这类元素可能是 POD。
以上所有保证都基于一个条件: 析构函数不得抛出异常。
std::deque
提供随机访问,接口与 std::vector
几乎一致。可以在首尾快速安插和删除。
通常实现为一组独立的区块,第一区块往一个方向发展,最末的区块往另一个方向发展。
std::deque
与 std::vector
比较
相同之处
- 在中段安插、移除元素的速度相对较慢,因为所有元素都需移动以腾出或填补空间。
- 迭代器属于随机访问迭代器,可以使用
operator[]
\at()
不同之处
std::deque
可在常量时间内快速在首尾增删元素。std::vector
只能在尾部。- 访问元素时,
std::deque
多了一个跳转的过程(在各个区块跳转) std::deque
的迭代器不是原始指针(因为各个区块之间不连续), 更没有data
的成员函数std::deque
不支持对容量和内存分配时机的控制。std::deque
在首尾两端增删元素导致所有元素的迭代器失效(指针和引用仍有效),其他所有增删操作都会导致所有元素的指针、引用和迭代器失效。std::deque
的内存分配优于std::vector
,std::deque
不必在内存分配时复制所有元素。std::deque
会释放不再使用的内存区块。std::deque
的内存大小是可缩减的, 但要不要这么做,以及如何做,由实现决定。std::deque
不提供容量操作capacity()
和reserve()
- 在内存区块大小有限制的系统中,
std::deque
的max_size()
可能比std::vector
的max_size()
要大。 因为std::deque
使用的不止一块内存。
适用场景
- 需要在两端安插和移除元素
- 无须指向容器内的元素
- 要求使用内存会自动缩小
异常处理
原则上 std::deque
提供的异常处理和 std::vector
提供的一样
push_front()
和push_back()
安插元素时发生异常,则该操作不带来任何效应。pop_front()
和pop_back()
不会抛出任何异常。
std::list
双向链表。
std::list
的内部结构完全迥异于 std::array
、std::vector
、std::deque
。 std::list
自身提供了两个指针,分别指向第一个元素和最后一个元素,如果操纵对应的指针即可。
成员函数 splice
示意
容器特性
- 提供
front()
、push_front()
、pop_front()
、back()
、push_back()
和pop_back()
等操作函数。 - 不提供
operator[]
或at()
- 不支持随机访问。O(n)
- 在任何位置插入元素非常快。O(1), 只是改变了指针指向。
- 迭代器永久有效。插入和删除动作并不会造成指向其他元素的指针、引用和迭代器失效。
- 异常安全
std::list
的异常处理为: 要么操作成功、要么什么都不发生。 - 事务安全。 只要不调用赋值操作或
sort()
, 并保证元素相互比较时不抛出异常那么std::list
可以成为事务安全 - 空间最优。没有空间重新分配和预分配内存, 没有冗余内存占用
- 拥有较多的特殊成员函数,相较于
STL
中通用的同名函数,更具有效率。如merge
、splice
、remove
、reverse
、unique
、sort
。
应用场景
特性: 前向迭代器
- 排序
1 | std::list<int> list; |
- 特殊的排序后显示
1 | std::list<int> list; |
特性: 迭代器永不失效
- 双键结构
1 | template <class LEFT_KEY, class RIGHT_KEY, class VALUE> |
std::forward_list
(C++11)
标准描述
std::forward_list
是支持从容器中的任何位置快速插入和移除元素的容器。不支持快速随机访问。它实现为单链表,且实质上与其在C
中实现相比无任何开销。与std::list
相比,此容器在不需要双向迭代时提供更有效地利用空间的存储。
与 std::list
比较
std::forward_list
只提供前向迭代器,而不是双向迭代器。没有成员函数rbegin()
、rend()
、crbegin()
和crend()
std::forward_list
不提供成员函数size()
。std::forward_list
没有指向最末元素的指针。所以没有成员函数如back()
、push_back()
和pop_back()
- 对于所有令元素被安插在或删除于的某特定位置上的成员函数,
std::forward_list
都提供特殊版本。原因是你必须传递第一个被处理元素的前一位置,前向迭代器不能回头。 insert_after()
代替insert()
, 也额外提供before_begin()
和cbefore_begin()
在起始处安插元素
std::set
和 std::multiset
std::set
和 std::multiset
会根据特定的排序准则,自动将元素排序。两者不同之处在于 std::multiset
允许元素重复而 std::set
不允许。
如果没有传入某个排序准则,就采用默认准则 std::less
以 operator<
对元素进行比较。
排序准则符合: 严格弱序
详细定义
必须是非对称的(antisymmetric)。
对
operator<
而言, 如果x < y为true, 则y < x为false。对判断式(predicate)
op()
而言,如果op(x, y)为true,则op(y, x)为false。必须是可传递的(transitive)。
对
operator<
而言,如果x < y 为true且y < z为true, 则x < z 为false。对判断式(predicate)
op()
而言,如果op(x, y)为true且op(y, z)为tru,则op(x, z)为true。
必须是非自反的(irreflexive)
对
operator<
而言,x < x 永远是false对判断式(predicate)
op()
而言,op(x, x)永远是false。必须有等效传递性(transitivity of equivalence)
对
operator<
而言,假如 !(a<b) && !(b<a) 为true且 !(b<c) && !(c<b) 为 true
那么!(a<c) && !(c<a) 也为true.
对判断式(predicate)op()
而言, 假如 op(a,b), op(b,a), op(b,c), 和op(c,b) 都为
false, 那么op(a,c) and op(c,a) 也为false.
简单的来说就是a<b返回true,a=b和a>b返回false。
定制排序规则 operator<
1 | class CALL_INFO_C |
其中 std::multiset
的等效元素的次序是随机但稳定的。(C++11以后标准保证新插入的元素,会被放在等效元素群的末尾)
std::set
和 std::multiset
的能力
- 通常以平衡二叉树完成。
- 自动排序的主要优点在于令二叉树于查找元素时拥有良好的性能。其查找函数具有
O(logn)
的时间复杂度。 - 不能随意改变元素值,因为这会打乱原本正确的顺序。
- 如果要改变元素值,必须先删除旧元素,再插入新元素。
- 不提供任何操作函数可以直接访问底层元素
- 通过迭代器进行元素间接访问,有一个限制: 从迭代器的角度看,元素值是常量.(例如不能使用:
std::remove()
) - 其迭代器是双向迭代器(不能使用
std::sort()
)
std::map
和 std::multimap
std::map
和 std::multimap
将 key/value pair
当作元素进行管理。它们可根据 key
的排序准则自动为元素排序。 std::multimap
允许重复元素, std::map
不允许。
同样 key
需要可比较且遵循严格弱序。
std::map
和 std::multimap
通常以平衡二叉树完成。
std::map
和 std::multimap
也无法改变 key
的值。只能删除再插入。
operator[]
若key
不存在, 构造该元素后,返回元素的引用
若key
存在, 返回元素的引用
所以要警惕如下语句:
1 | std::map<std::string, int> map; |
1 | std::map<std::string, int> map; |
也可以利用这一特性用来计数:
1 | std::map<char, int> map; |
无序容器 (Unordered Container) C++11
C++11 之前因为标准库中没有哈希表类的数据结构,所以很多程序库自己实现了诸如 hash_set
、hash_multiset
、hash_map
、hash_multimap
。
为了避免名称冲突,C++11 标准采用了不一样的名称,使用统一前缀 unordered_
, 即unordered_set
、 unordered_multiset
、unordered_map
、unordered_multimap
。
unordered_set
、 unordered_multiset
、unordered_map
、unordered_multimap
底层实现都是哈希表,所以 key
需要可哈希。
但是在链表是单链还是双链(意味着其迭代器可能不是双向迭代器),重新哈希的时机这些都没有指定,根据实现而定。
定制哈希示例
1 | class MY_HASH |
容器特性
安插、删除、查找元素大部分是 O(1), 但偶尔发生的重新哈希时间复杂度变为 O(n)
由于其迭代器只保证至少为前向迭代器,因此不提供包括 rbegin()
、 rend()
以及不能使用要求双向迭代器的的 STL 函数如std::sort()
、std::binary_search()
你可以手动强制重新哈希。
重新哈希可能发生在以下调用之后: insert()
、rehash()
、rehash()
或 clear()
。
erase()
函数并不会令指向其他元素的指针、引用和迭代器失效。
insert()
和 emplace()
可能令所有迭代器失效。但不会影响引用的有效性。
当重新哈希过程发生,元素的引用仍然有效。
应用场景
假如缓存中,我们缓存若干最近访问和删除的记录至内存用于快速访问,使得插入记录和读取最近的记录的时间复杂度为 O(1)
。
- LRU (Least recently used)
设计接口
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该擦除最久未使用的关键字。- 函数
get
和put
必须以O(1)
的平均时间复杂度运行。
设计思路:
1 |
|
std::unordered_map
特性: 单向迭代器, 增删元素 O(1), 增删元素后迭代器可能失效
std::list
特性: 迭代器永不失效,任意位置插入常量时间 O(1), 访问首尾元素 O(1)
完整代码
1 | class LRUCache |
特殊容器
std::string
其被定义为: std::basic_string<char>
其中模板入参 char
可以换为 unsigned char
或 wchar
std::stack
后进先出
std::stack
定义如下:
1 | template <typename T, |
其底层类型默认为 std::deque
之所以不选择 std::vector
是因为在内存管理上 std::deque
比 std::vector
更有效率。
1 | template <typename T, |
std::queue
先进先出
底层实现默认采用 std::queue
1 | template <typename T, |
1 | template <typename T, |
std::priority_queue
实现出一个队列,其中的元素按优先级存储。
1 | template <typename T, |
应用实例:
求数据流中的中位数
1 | class MedianFinder |
std::bitset
std::bitset
内含一个元素值为 bit
或 bool
值且大小固定的 array
。当你需要管理各式flag
, 并以 flag
的任意组合来表现变量时, 就可运用 std::bitset
。
可容纳任意个数的标志位(编译期确定数量)
std::bitset
编译期确定大小 std::vector<bool>
可动态增长。
桶式排序
给 1000 个数字排序, 数字范围 [0, 99]
1 | unsigned a[1000] = {10, 99, 4, 4, 5, 6, 7}; |
给 40 亿个 unsigned
数字([0, 99])中寻找不存在的数值
汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。多用于数据传输中的差错控制。
1 | /// 0 <= x, y <= 2^31 - 1 |
迭代器介绍
Iterator
(迭代器)是一种”能够迭代某序列内所有元素”的对象,可通过改变自寻常pointer的一致性接口来完成工作。Iterator
奉行一个纯抽象概念:任何东西,只要行为类似iterator,就是一种iterator。然而不同的的iterator具有不同的行进能力。
迭代器种类
迭代器种类 | 能力 | 提供者 |
---|---|---|
Output 迭代器 | 向前写入 | Ostream,inserter |
Input 迭代器 | 向前读取一次 | Istream |
Forward 迭代器 | 向前读取 | Forward list、unordered containers |
Bidirectional 迭代器 | 向前和向后读取 | List、set、multiset、map、multimap |
Random-access 迭代器 | 以随机访问方式读取 | Array、vector、deque、string、C-style array |
Output迭代器允许一步一步前行并搭配write动作。因此你可以一个一个元素地赋值,不能使用output迭代器对同一区间迭代两次。事实上,甚至不保证你可以将一个value复制两次而其迭代器不累进。我们的目标是将一个value以下列形式写入一个黑洞。
1 | while(...) { |
Output 迭代器无需比较操作。你无法检验output迭代器是否有效,或写入是否成功。你唯一可做的就是写入。通常,一批写入动作是以一个”额外条件定义出”的”特定output迭代器”作为结束。
见下表Output迭代器操作
表达式 | 效果 |
---|---|
*iter = val | 将val写至迭代器所指的位置 |
++iter | 向前步进(step forward), 返回新位置 |
iter++ | 向前步进(step forward), 返回旧位置 |
TYPE(iter) | 复制迭代器(copy 构造函数) |
Input迭代器
Input迭代器只能一次一个以前行方向读取元素,按此顺序一个个返回元素值。
Input迭代器的各项操作
表达式 | 效果 |
---|---|
*iter | 读取实际元素 |
iter->member | 读取实际元素的成员(如果有的话) |
++iter | 向前步进(step forward), 返回新位置 |
iter++ | 向前步进(step forward), 返回旧位置 |
iter1 == iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不相等 |
TYPE(iter) | 复制迭代器(copy 构造函数) |
Input迭代器只能读取元素一次。如果你复制input迭代器, 并令原input迭代器和新产生的拷贝都向前读取, 可能会遍历到不同的值。
所有的迭代器都具备input迭代器的能力,而且往往更强。Pure input
迭代器的典型例子就是”从标准输入设备读取数据”。同一个值不会被读取两次。一旦从input stream
读入一个字(离开input缓冲区), 下次读取时就会返回另一个字。
对于input迭代器, 操作符==和!=只用来检查”某个迭代器是否等于一个past-the-end迭代器(指指向最末元素的下一个位置)”.这有其必要, 因为处理input迭代器的操作函数通常会有以下行为。
1 | InputIterator pos, end; |
没有任何保证说,两个迭代器如果都不是past-the-end迭代器, 且指向不同位置,他们的比较结果会不相等(这个条件是和forward迭代器搭配引入的)。
也请注意, input迭代器的后置式递增操作符(++iter
)不一定会返回什么东西。不过通常它会返回旧位置。
你应该尽可能优先先选用前置式递增操作符(++iter
)而非后置式递增操作符(iter++
), 因为前者效能更好。因为后者会返回一个临时对象。
Forward(前向)迭代器
Forward迭代器是一种input迭代器且在前进读取时提供额外保证。
表达式 | 效果 |
---|---|
*iter | 访问实际元素 |
iter->member | 访问实际元素的成员 |
++iter | 向前步进(返回新位置) |
iter++ | 向前步进(返回旧位置) |
iter1 == iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不等 |
TYPE() | 创建迭代器(default构造函数) |
TYPE(iter) | 复制迭代器(拷贝构造函数) |
iter1 = iter2 | 对迭代器赋值(assign) |
和input迭代器不同的是, 两个forward迭代器如果指向同一元素, operator== 会获得true , 如果两者都递增, 会再次指向同一元素。 |
|
例如: |
1 | ForwardIterator pos1, pos2; |
Forward迭代器由以下对象和类型提供:
- Class
- Unordered container
然而标准库也允许unordered
容器的实现提供bidirectional
迭代器。
如果forward迭代器履行了output迭代器应有的条件, 那么它就是一个mutable forward迭代器, 即可用于读取,也可用于涂写。
Random-Access(随机访问)迭代器
Random-access
迭代器在bidirectional
迭代器的基础上增加了随机访问能里。因此它必须提供iterator
算数运算。也就是说,它能增减某个偏移量、
计算距离(difference
), 并运用诸如<和>等管理操作符(relational operator
)进行比较。
随机访问迭代器的新增操作:
表达式 | 效果 |
---|---|
iter[n] | 访问索引位置为n的元素 |
iter+=n | 前进n个元素(如果n是负数, 则改为回退) |
iter-=n | 回退n个元素(如果n是负数, 则改为前进) |
iter+n | 返回iter之后的第n个元素 |
n+iter | 返回iter之后的第n个元素 |
iter-n | 返回iter之前的第n个元素 |
iter1-iter2 | 返回iter1和iter2之间的距离 |
iter1 < iter2 | 判断iter1是否在iter2之前 |
iter1 > iter2 | 判断iter1是否在iter2之后 |
iter1 <= iter2 | 判断iter1是否不在iter2之后 |
iter1 >= iter2 | 判断iter1是否不在iter2之前 |
Random-access
迭代器由以下对象和类型提供:
- 可随机访问的容器(
array
、vector
、deque
) - String(
string
、wstring
) - 寻常的C-Style(
pointer
)
迭代器应用
判断字符串是否为回文。
1 | constexpr bool is_palindrome(const std::string_view& s) |
1 | bool is_palindrome(const std::string& s) |