9.1 顺序容器概述
顺序容器:为程序员提供了 控制元素存储和访问顺序 的能力。这种顺序不依赖于 元素的值,而是与元素加入容器时 的位置相对应。
顺序容器类型 | 介绍 |
vector | 可变大小数组。支持快速随机访问。在尾部之外的位置 插入或删除元素可能很慢。 |
deque | 双端队列。支持快速随机访问。在头尾位置 插入/删除速度很快。 |
list | 双向链表。只支持双向顺序访问。在list中任何位置 进行插入/删除操作速度都很快。 |
forward_list | 单向链表。只支持单向顺序访问。在链表任何位置 进行插入/删除操作速度都很快。 |
array | 固定大小数组。支持快速随机访问。不能添加或者删除元素。 |
string | 与vector相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。 |
9.2 容器库概览
容器操作 | |
类型别名: | |
iterator | 此容器类型的 迭代器类型 |
const_iterator | 可以读取元素 但不能修改元素 的迭代器类型 |
size_type | 无符号整数类型,足够保存此种容器类型 最大可能的大小 |
difference_type | 带符号整数类型,足够保存两个迭代器之间的距离 |
value_type | 元素类型 |
reference | 元素的左值类型(和value_type &含义相同) |
const_reference | 元素的const左值类型(即const value_type &) |
构造函数: | |
C c; | 默认构造函数,构造空容器 |
C c1(c2);或 C c1 = c2; | 构造c2的拷贝c1 |
C c(b, e); | 构造c,将迭代器b和e指定范围内 的所有元素拷贝到c |
C c{ a, b, c...} | 列表初始化c |
C c(n) | 包含n个元素,这些元素进行了 值初始化(只支持顺序容器,且不包括array) |
C c(n, t) | 包含n个初始值为t的元素 |
赋值和swap: | |
c1 = c2; | 将c1中的元素替换成 c2中的元素 |
c1 = {a, b, c...} | 将c1中的元素替换成 列表中的元素(不适用于array) |
c1.swap(c2) | 交换c1和c2的元素 |
swap(c1, c2) | 等价于c1.swap(c2) |
c.assign(b, e) | 将c中的元素替换成 迭代器b和e表示范围中 的元素,b和e不能指向c中的元素 |
c.assign(il) | 将c中的元素替换成 初始化列表il 中的元素 |
c.assign(n, t) | 将c中的元素替换成 n个值是t 的元素 |
大小: | |
c.size() | c中元素的数目(不支持forward_list) |
c.max_size() | c中可保存的最大元素数目 |
c.empty() | 若c中存储了元素,返回false,否则返回true |
添加元素: | |
c.push_back(t) | 在c尾部创建一个 值为t的元素,返回void |
c.emplace_back(args) | 同上(效率更高) |
c.push_front(t) | 在c头部创建一个 值为t的元素,返回void |
c.emplace_front(args) | 同上 |
c.insert(p, t) | 在迭代器p指向的元素之前 创建一个值是t的元素,返回指向新元素的迭代器 |
c.emplace(p, args) | 同上 |
c.insert(p, n, t) | 在迭代器p指向的元素之前 插入n个值为t的元素,返回指向 第一个新元素的迭代器。如果n是0,则返回p |
c.insert(p, b, e) | 在迭代器p指向的元素之前 插入迭代器b和e范围内的元素。如果范围为空,则返回p |
c.insert(p, il) | il是一个被花括号包围的 元素值列表,将其插入到p指向的元素之前。如果il是空,则返回p |
删除元素: | |
c.pop_back() | 删除c中尾元素。若c为空,则函数行为未定义。函数返回void |
c.pop_front() | 删除c中首元素。若c为空,则函数行为未定义。函数返回void |
c.erase(p) | 删除迭代器p指向的元素,返回一个 指向被删除元素之后的元素的 迭代器。若p本身是尾后迭代器,则函数行为未定义 |
c.erase(b, e) | 删除迭代器b和e范围内的元素,返回一个 指向最后一个被删元素之后的元素的 迭代器。若e本身就是尾后迭代器,则返回尾后迭代器 |
c.clear() | 删除c中所有元素,返回void |
访问元素: | |
c.back() | 返回c中尾元素的引用。若c为空,函数行为未定义 |
c.front() | 返回c中头元素的引用。若c为空,函数行为未定义 |
c[n] | 返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义 |
c.at(n) | 返回c中下标为n的元素的引用。如果下标越界,则抛出out_of_range异常 |
特殊的forwad_list操作: | |
lst.before_begin() | 返回指向链表首元素之前 不存在的元素的迭代器,此迭代器不能解引用。 |
lst.cbefore_begin() | 同上。但是返回的是常量迭代器 |
lst.insert_after(p, t) | 在迭代器p之后 插入元素t。t是一个对象 |
lst.insert_after(p, n, t) | 在迭代器p之后 插入n个元素t。若n为0,则函数行为未定义 |
lst.insert_after(p, b, e) | 在迭代器p之后 插入迭代器b和e指定范围的元素 |
lst.insert_after(p, il) | 在迭代器p之后 插入由il指定初始化列表组成的元素 |
emplace_after(p, args) | 在迭代器p之后 插入一个元素args,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义 |
lst.erase_after(p) | 删除迭代器p指向位置之后的元素,返回一个指向 被删元素之后的元素 的迭代器。若p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义 |
lst.erase_after(b, e) | 删除迭代器b和e指定范围的元素 |
改变容器大小: | |
c.resize(n) | 调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃;若必须添加新元素,对新元素进行值初始化 |
c.resize(n, t) | 调整c的大小为n个元素,任何新添加的元素 都初始化为值t |
获取迭代器: | |
c.begin(),c.end() | 返回指向c的 首元素和尾元素 之后位置的迭代器 |
c.cbegin(),c.cend() | 同上。但是返回常量迭代器 |
反向容器的额外成员:(不支持forward_list) | |
reverse_iterator | 按逆序寻址元素的迭代器 |
const_reverse_iterator | 同上。但是不能修改元素的逆序迭代器 |
c.rbegin(),c.rend() | 返回指向c的 尾元素和首元素 之前位置的迭代器 |
c.crbegin(),c.crend() | 同上。但是返回常量迭代器 |
(1) 迭代器:
要访问 顺序容器和关联容器 中的元素,需要通过 “迭代器(iterator)” 进行。迭代器是一个变量,相当于 容器和操纵容器的算法 之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。(迭代器和指针类似)
- 迭代器范围:begin到end,即第一个元素到最后一个元素的后面一个位置。
- 左闭合区间:[begin, end)
c.begin(),c.end() | 返回指向c的 首元素和尾元素 之后位置的迭代器 |
c.cbegin(),c.cend() | 同上。但是返回常量迭代器 |
(2) begin和end成员:
int main() { list<string> a = { "aaa","bbb","ccc" }; const list<string> b = { "AAA","BBB","CCC" }; auto itl = a.begin(); // list::iterator auto it2 = a.rbegin(); // list::reverse_iterator auto it3 = a.cbegin(); // list::const_iterator auto it4 = a.crbegin(); // list::const_reverse_iterator auto it5 = a.begin(); // list::iterator auto it6 = b.begin(); // list::const_iterator }
建议:当不需要写访问时,应使用cbegin和cend。
(3) 容器定义和初始化:
C c; | 默认构造函数,构造空容器 |
C c1(c2);或 C c1 = c2; | 构造c2的拷贝c1 |
C c(b, e); | 构造c,将迭代器b和e指定范围内 的所有元素拷贝到c |
C c{ a, b, c...},C c={ a, b, c...} | 列表初始化c |
C c(n) | 包含n个元素,这些元素进行了 值初始化(只支持顺序容器,且不包括array) |
C c(n, t) | 包含n个初始值为t的元素 |
- 直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。
- 使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同,只要能将 要拷贝的元素类型 转换为 要初始化的容器的元素类型 即可。
- 只有顺序容器的构造函数 才接受大小参数,关联容器并不支持。
- array具有固定大小。(当定义一个array时 除了指定元素类型,还要指定容器大小)
- 和其他容器不同,默认构造的array是非空的,它包含了 与其大小一样多 的元素,这些元素都被默认初始化。
int main() { list<string> lst = { "aaa","bbb","ccc" }; deque<const char*> deq = { "AAA","BBB","CCC" }; list<string> lst1(lst); // 正确: 容器类型和元素类型 都相同 list<string> lst2(deq); // 错误: 容器类型和元素类型 都不相同 list<string> lst3(lst.begin(), lst.end()); // 正确 list<string> lst4(deq.begin(), deq.end()); // 正确: const char*可以转换成string vector<string> vec(5); // 正确 set<string> vec(5); // 错误: 只有顺序容器的构造函数 才接受大小参数,关联容器并不支持。 array<int> arr1; // 错误: 当定义一个array时 除了指定元素类型,还要指定容器大小 array<int,5> arr2; // 正确: arr2包含了 与其大小一样多 的元素,这些元素都被默认初始化 array<int,10> arr3 = arr2; // 错误: 大小不匹配(大小和类型都要匹配) array<int,5> arr4 = arr2; // 正确 }
(4) 赋值和swap:
c1 = c2; | 将c1中的元素替换成 c2中的元素 |
c1 = {a, b, c...} | 将c1中的元素替换成 列表中的元素(不适用于array) |
c1.swap(c2) | 交换c1和c2的元素 |
swap(c1, c2) | 等价于c1.swap(c2) |
c.assign(b, e) | 将c中的元素替换成 迭代器b和e表示范围中 的元素,b和e不能指向c中的元素 |
c.assign(il) | 将c中的元素替换成 初始化列表il 中的元素 |
c.assign(n, t) | 将c中的元素替换成 n个值是t 的元素 |
- 统一使用 非成员版本的swap 是一个好习惯。(swap通常比从c2向c1拷贝元素快得多)
- 交换两个容器内容的操作会很快——元素本身并未被交换,非成员版本的swap 只是交换了两个容器的内部数据结构。(除了array,swap两个array会真正交换它们的元素)
- 因此元素不会被移动,这意味着 指向窗口的迭代器、引用和指针在 非成员版本swap操作之后都不会失效。它们仍指向swap操作之前 所指向的那些元素,只是这些元素已经属于不同的容器了。(除了string,对一个string调用swap会导致迭代器、引用和指针失效。)
- assign操作不适用于 关联容器和array
(5) 容器大小操作:
c.size() | c中元素的数目(不支持forward_list) |
c.max_size() | c中可保存的最大元素数目 |
c.empty() | 若c中存储了元素,返回false,否则返回true |
(6) 关系运算符:
比较两个容器实际上是进行 元素的逐对比较。
int main() { vector<int> v1 = { 1,2,3,4,5 }; vector<int> v2 = { 1,2,5 }; vector<int> v3 = { 1,2,3,4 }; vector<int> v4 = { 1,2,3,4,5 }; v1 < v2; // ture: v1和v2在元素[2]处不同,v1[2]小于v2[2] v1 > v3; // ture: 所有元素都相等,但v1中元素数目更多 v1 == v4; // true: 每个元素都相等,且v1和v4大小相同 v1 == v3; // false: v1元素数目比v3多 vector<Demo> d1; vector<Demo> d2; d1 == d2; // 如果Demo没有定义 比较运算符,则不能使用 关系运算符==比较两个容器 }
注:只有当容器元素类型也定义了相应的 比较运算符时,我们才可以使用 关系运算符来比较两个容器。
9.3 顺序容器操作
(1) 向顺序容器添加元素:c.push_back(t) | 在c尾部创建一个 值为t的元素,返回void |
c.emplace_back(args) | 同上(效率更高) |
c.push_front(t) | 在c头部创建一个 值为t的元素,返回void |
c.emplace_front(args) | 同上 |
c.insert(p, t) | 在迭代器p指向的元素之前 创建一个值是t的元素,返回指向新元素的迭代器 |
c.emplace(p, args) | 同上 |
c.insert(p, n, t) | 在迭代器p指向的元素之前 插入n个值为t的元素,返回指向 第一个新元素的迭代器。如果n是0,则返回p |
c.insert(p, b, e) | 在迭代器p指向的元素之前 插入迭代器b和e范围内的元素。如果范围为空,则返回p |
c.insert(p, il) | il是一个被花括号包围的 元素值列表,将其插入到p指向的元素之前。如果il是空,则返回p |
- 当我们用一个对象 去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。
- 因为这些操作会改变大小,因此不适用于array。
- forward_list有自己专有版本的 insert和emplace。forward_list不支持 push_back和emplace_back。
- emplace开头的函数是新标准引入的,这些操作是 构造而不是拷贝元素。传递给emplace的参数 必须和元素类型的构造函数相匹配。
int main() { vector<Sales_data> vec; vec.pop_back("978-0590353403",25, 15.99); // 错误: 没有接受三个参数的push_ back版本 vec.emplace_back("978-0590353403",25, 15.99); // 正确: 使用三个参数的Sales data构造函数 vec.push_back(Sales_ data("978-0590353403",25, 15.99)); // 正确: 创建一个临时的Sales_ data对象传递给push_ back }
(2) 访问元素:
c.back() | 返回c中尾元素的引用。若c为空,函数行为未定义 |
c.front() | 返回c中头元素的引用。若c为空,函数行为未定义 |
c[n] | 返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义 |
c.at(n) | 返回c中下标为n的元素的引用。如果下标越界,则抛出out_of_range异常 |
- at和下标操作只适用于string、vector、deque、array。
- back不适用于forward_list。
- 访问成员函数返回的是引用。
- 如果希望下标是合法的,可以使用at函数。(at成员函数类似下标运算符,但如果下标越界,at会抛出一个out_of_range 异常)
int main() { vector<int> vec = { 1,2,3 }; if (!vec.empty()) { vec.front() = 10; // 将10赋予vec中的第一个元素vec[0] auto& v1 = vec.back(); // 获得指向最后一个元素的引用 v1 = 10; // 将vec中的元素vec[2]的值改为10 auto v2 = vec.back(); // v2不是一个引用,它是c.back()的一个拷贝(auto根据编译器 计算变量初始值推断类型,会忽略&) v2 = 10; // 未改变vec中的元素 } }
(3) 删除元素:
c.pop_back() | 删除c中尾元素。若c为空,则函数行为未定义。函数返回void |
c.pop_front() | 删除c中首元素。若c为空,则函数行为未定义。函数返回void |
c.erase(p) | 删除迭代器p指向的元素,返回一个 指向被删除元素之后的元素的 迭代器。若p本身是尾后迭代器,则函数行为未定义 |
c.erase(b, e) | 删除迭代器b和e范围内的元素,返回一个 指向最后一个被删元素之后的元素的 迭代器。若e本身就是尾后迭代器,则返回尾后迭代器 |
c.clear() | 删除c中所有元素,返回void |
- 会改变容器大小,不适用于array。
- forward_list有特殊版本的erase,forward_list不支持pop_back。
- vector和string不支持pop_front。
(4) 特殊的forwad_list操作:
- 链表在删除元素时需要 修改前置节点的内容,双向链表结点 有前驱的指针,但是单向链表结点 没有,出于这个原因,在一个forward list 中添加或删除元素的操作是通过 改变给定元素之后的元素 来完成的。
- forward_list定义了before_begin,即首前迭代器,允许我们再在首元素之前添加或删除元素。
lst.before_begin() | 返回指向链表首元素之前 不存在的元素的迭代器,此迭代器不能解引用。 |
lst.cbefore_begin() | 同上。但是返回的是const_iterator |
lst.insert_after(p, t) | 在迭代器p之后 插入元素t。t是一个对象 |
lst.insert_after(p, n, t) | 在迭代器p之后 插入n个元素t。若n为0,则函数行为未定义 |
lst.insert_after(p, b, e) | 在迭代器p之后 插入迭代器b和e指定范围的元素 |
lst.insert_after(p, il) | 在迭代器p之后 插入由il指定初始化列表组成的元素 |
emplace_after(p, args) | 在迭代器p之后 插入一个元素args,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义 |
lst.erase_after(p) | 删除迭代器p指向位置之后的元素,返回一个指向 被删元素之后的元素 的迭代器。若p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义 |
lst.erase_after(b, e) | 删除迭代器b和e指定范围的元素 |
int main() { std::forward_list<int> f_list{ 1, 2, 3 ,4, 5 }; auto prev = f_list.before_begin(); auto curr = f_list.begin(); while (curr!=f_list.end()) { if (*curr % 2 == 0) { // 如果curr指向元素为偶数,则删除(利用curr指向结点的 之前结点的指针prev) curr = f_list.erase_after(prev); // curr为被删除元素之后元素的迭代器 } else { // 否则prev,curr同时向后移动,继续寻找 prev = curr; ++curr; } } for (auto x : f_list) { std::cout << x << " "; // 1,3,5 } }
c.resize(n) | 调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃;若必须添加新元素,对新元素进行值初始化 |
c.resize(n, t) | 调整c的大小为n个元素,任何新添加的元素 都初始化为值t |
注:resize成员函数 只改变容器中元素的数目,而不是容器的容量。(我们不能使用resize来减少 容器预留的内存空间。若调用resize时,给定的大小n 超过当前容器容量,则vector将扩大容器容量)
int main() { list<int> lst(10, 1); // 10个int,每个的值都是1 lst.resize(15); // 将5个值为0的元素 添加到lst的末尾 lst.resize(20, -1); // 将5个值为-1的元素 添加到lst的末尾 lst.resize(5); // 从lst末尾删除15个元素 }
(6) 容器操作可能使迭代器失效:
在向容器中添加元素后:
建议:不要保存end返回的迭代器。(当我们添加/删除vector或string的元素后,或在deque中首元素之外 任何位置添加/删除元素后,原来end返回的迭代器总是会失效)
在向容器中添加元素后:
- 如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针、引用都会失效。
- 对于deque,插入到除首尾位置之外的任何位置都会导致指向容器的迭代器、指针、引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在元素的引用和指针不会失效。
- 对于list和forward_list,指向容器的迭代器、指针和引用依然有效。
- 对于list和forward_list,指向容器其他位置的迭代器、引用和指针仍然有效。
- 对于deque,如果在首尾之外的任何位置 删除元素,那么指向被删除元素外 其他元素的迭代器、指针、引用都会失效;如果是删除deque的尾元素,则尾后迭代器会失效,但其他不受影响;如果删除的是deque的头元素,这些也不会受影响。
- 对于vector和string,指向被删元素之前 的迭代器、引用、指针仍然有效。
建议:不要保存end返回的迭代器。(当我们添加/删除vector或string的元素后,或在deque中首元素之外 任何位置添加/删除元素后,原来end返回的迭代器总是会失效)
(7) 容器内元素的类型约束:
- 元素类型必须支持 赋值运算;
- 元素类型的对象必须 可以复制。
- 除了输入输出标准库类型外,其他所有标准库类型 都是有效的容器元素类型。
9.4 vector对象是如何增长的
vector和string在内存中是 连续保存的,如果原先分配的内存位置已经使用完,则需要重新分配新空间,将已有元素 从旧位置移动到新空间中,然后添加新元素。
管理容量的成员函数:
c.shrink_to_fit() | 将capacity()减少到和size()相同大小 |
c.capacity() | 不重新分配内存空间的话,返回c可以保存元素的最大个数 |
c.reverse(n) | 分配至少能容纳n个元素的内存空间 |
- shrink_to_fit 只适用于vector、string、deque。
- capacity和reverse 只适用于vector、string。
- 只有当需要的内存空间 超过当前容量时,reserve调用才会改变vector的容量。如果需求大小大于当前容量,reserve至少分配 与需求一样大的内存空间 (可能更大)。
- 我们可以调用shrink_to_fit 来要求 deque. vector或string 退回不需要的任何多余的内存空间。但是,具体的实现可以选择忽略此请求。(调用shrink_to_fit 并不保证一定退回内存空间)
- 只有在执行insert操作时 size与capacity相等,或者调用resize或reserve时 给定的大小超过当前capacity,vector才可能重新分配内存空间。(会分配多少 超过给定容量的额外空间,取决于具体实现)
9.5 额外的string操作
string s(cp, n) | s是 cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符 |
string s(s2, pos2) | s是 string s2从下标pos2开始的字符的拷贝。若pos2 > s2.size(),则构造函数的行为未定义。 |
string s(s2, pos2, len2) | s是 string s2从下标pos2开始的 len2个字符的拷贝。 |
int main() { const char* cp = "Hello World!!!"; // 以空字符结束的数组 char noNull[] = { 'H','i' }; // 不是以空字符结束的数组 string s1(cp); // 正确: 拷贝cp中的字符直到遇到空字符,s1 == "Hello World!! !" string s2(noNull); // 错误: noNull不是以空字符结束 string s3(noNull, 2); // 正确: 从noNull拷贝两个字符,s2 == "Hi" string s4(s1, 6); // 正确: 从s1[6]开始拷贝,直至s1末尾; s6== "World!!!" string s5(cp + 6, 5); // 正确: 从cp[6]开始拷贝5个字符,s4 == "World" string s6(s1, 16); // 错误: 抛出一个out_ of_ range异常(16>=s1.size) string s7(s1, 6, 5); // 正确: 从s1[6]开始拷贝5个字符,s5 == "World" string s8(s1, 6, 20); // 正确: 从s1[6]开始拷贝,但只拷贝到s1末尾; s7 == "World!!!" }
s.substr(pos, n) | 返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值是0,n的默认值是s.size() - pos,即拷贝从pos开始的所有字符。 |
(3) 改变string的其他方法:
s.insert(pos, args) | 在pos之前插入 args指定的字符,pos可以使是下标或者迭代器。接受下标的版本返回 指向s的引用;接受迭代器的版本返回 指向第一个插入字符的迭代器。 |
s.erase(pos, len) | 删除从pos开始的 len个字符。如果len被省略,则删除后面所有字符,返回指向s的引用。 |
s.assign(args) | 将s中的字符替换成 args指定的字符。返回一个指向s的引用。 |
s.append(args) | 将args指定的字符 追加到s,返回一个指向s的引用。 |
s.replace(range, args) | 删除s中范围range中的字符,替换成args指定的字符。返回一个指向s的引用。(range是 一个下标和一个长度,或者是 一对指向s的迭代器) |
(4) string搜索操作:
string类提供了 6个不同的搜索函数,每个函数都有 4个重载版本。每个搜索操作都返回一个 string::size_type值,表示匹配发生位置的下标。如果搜索失败 则返回一个名为string::npos的static成员(nops的类型是 const string::size_type,初始化值是-1,也就是string最大的可能大小)。
s.find(args) | 查找s中 args第一次出现的位置 |
s.rfind(args) | 查找s中 args最后一次出现的位置 |
s.find_first_of(args) | 在s中查找args中 任何一个字符第一次出现的位置 |
s.find_last_of(args) | 在s中查找args中 任何一个字符最后一次出现的位置 |
s.find_first_not_of(args) | 在s中查找 第一个不在args中的字符 |
s.find_first_not_of(args) | 在s中查找 最后一个不在args中的字符 |
args必须是以下的形式之一: | |
c, pos | 从s中位置pos 开始查找字符c。pos默认是0 |
s2, pos | 从s中位置pos 开始查找字符串s。pos默认是0 |
cp, pos | 从s中位置pos 开始查找 指针cp指向的以空字符结尾的C风格字符串。pos默认是0 |
cp, pos, n | 从s中位置pos 开始查找 指针cp指向的数组的前n个字符。pos和n无默认值。 |
int main() { string str("abcdefghij"); string str1("12j34"); auto pos1 = str.find("cde"); // pos1=2(从str[2]开始匹配到) auto pos2 = str.find("CDE"); // 未定义(无匹配,大小写是敏感的!) auto pos3 = str1.find_first_of(str); // pos3=2(在str1[2]位置处 第一次出现 str中的字符) auto pos4 = str1.find_first_not_of(str); // pos4=0(在str1[0]位置处 第一次匹配不到 str中的字符) }
(5) s.compare函数:
逻辑类似于 C标准库的strcmp函数,根据s是等于、大于还是小于参数指定的字符串,s.compare返回0、正数或负数。
compare的几种参数形式: | |
s2 | 比较s和s2 |
pos1, n1, s2 | 将s中从pos1开始的n1个字符 与s2进行比较 |
pos1, n1, s2, pos2, n2 | 将s中从pos1开始的n1个字符 与s2中从pos2开始的n2个字符 进行比较 |
cp | 比较s和 cp指向的以空字符结尾的字符数组 |
pos1, n1, cp | 将s中从pos1开始的n1个字符 与cp指向的以空字符结尾的字符数组 进行比较 |
pos1, n1, cp, n2 | 将s中从pos1开始的n1个字符 与指针cp指向的地址开始的n2个字符 进行比较 |
(6) string和数值之间的转换:
to_string(val) | 一组重载函数,返回数值val的string表示。val可以使任何算术类型,对每个浮点类型和int或更大的整型,都有相应版本的to_string()。和往常一样,小整型会被提升。 |
stoi(s, p, b) | 返回s起始子串(表示整数内容)的数值,返回值类型为int。p是size_t指针,用来保存s中第一个非数值字符的下标,默认是0;b是转换所用的基数,默认值是10。 |
stol(s, p, b) | 同上,返回值类型为long |
stoul(s, p, b) | 同上,返回值类型为unsigned long |
stoll(s, p, b) | 同上,返回值类型为long long |
stoull(s, p, b) | 同上,返回值类型为unsigned long long |
stof(s, p) | 返回s起始子串(表示浮点数内容)的数值,返回值类型为float。p是size_t指针,用来保存s中第一个非数值字符的下标,默认是0。 |
stod(s, p) | 同上,返回值类型为double |
stold(s, p) | 同上,返回值类型为long double |
9.6 容器适配器
适配器是 使一事物的行为类似于另一事物的行为 的一种机制(例如stack可以使任何一种顺序容器以栈的方式工作)除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、 queue、priority_ queue。(默认情况下,stack、queue是基于deque实现的,priority_queue是基于vector实现的)
适配器(adaptor) 是标准库中的一个通用概念。 容器、迭代器和函数都有适配器。
适配器的通用操作和类型:
size_type | 一种类型,足以保存当前类型 的最大对象的大小 |
value_type | 元素类型 |
container_type | 实现适配器的底层容器类型 |
A a; | 创建一个名为a的空适配器 |
A a(c); | 创建一个名为a的适配器,带有容器c的一个拷贝 |
关系运算符 | 每个适配器都支持所有关系运算符:==、!=、<、 <=、>、>=,这些运算符返回底层容器的比较结果 |
a.empty() | 若a包含任何元素,返回false;否则返回true |
a.size() | 返回a中的元素数目 |
swap(a, b) | 交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同 |
a.swap(b) | 同上 |
int main() { vector<string> vec = { "aaa","bbb","ccc" }; stack<string, vector<string>> str_stk1; // 在vector上实现的空栈str_stk1 stack<string, vector<string>> str_stk2(vec); // 栈str_stk2在vector上实现,初始化时保存vec的拷贝 str_stk1.size(); // 0 str_stk2.size(); // 3 std::swap(str_stk1, str_stk2); str_stk1.size(); // 3 str_stk2.size(); // 0 str_stk1.top(); // "ccc" str_stk1.pop(); str_stk1.top(); // "bbb" str_stk1.pop(); str_stk1.top(); // "ccc" str_stk1.pop(); str_stk1.size(); // 0 }
注意:所有适配器不能构造在 array和forward_list 之上。(所有适配器都要求容器具有 添加、删除、访问尾元素 的能力)
- stack适配器:要求push_back、pop_back、back。(因此可以使用除array和forward_ list之外的任何容器类型来构造stack)
- queue适配器:要求back、push_back、front、push_front。(因此它可以构造于list或deque之上,但不能基于vector构造 (vector不支持push_front))
- priority_queue适配器:要求front、push_ back、pop_ back、随机访问能力。(因此它可以构造于vector或deque之上,但不能基于list构造 (list不支持随机访问))
栈(stack)操作:
定义在stack头文件中。stack默认基于deque实现,也可以在list或vector之上实现。
s.pop() | 删除栈顶元素。但不返回该元素 |
s.push(item) | 创建一个新元素,压入栈顶。该元素通过 拷贝或移动item而来 |
s.emplace(args) | 同上。但元素由args构造而来 |
s.top() | 返回栈顶元素。但不弹出该元素 |
队列(queue)和优先级队列(priority_queue)操作:
定义在queue头文件中。queue默认基于deque实现,也可以list或vector之上实现;priority_queue默认基于vector实现,也可以在deque之上实现。
q.pop() | 返回queue的首元素 或priority_ queue的最高优先级的元素。但不删除该元素 |
q.front() | 返回首元素。但不删除该元素 |
q.back() | 返回尾元素。但不删除该元素 |
q.top() | 返回最高优先级元素。但不删除该元素 |
q.push(item) | 在queue末尾 或priority_queue中恰当的位置 创建一个元素,其值为item |
q.emplace(args) | 同上。其值由args构造 |