9.1 顺序容器概述

顺序容器:为程序员提供了 控制元素存储和访问顺序 的能力。这种顺序不依赖于 元素的值,而是与元素加入容器时 的位置相对应。
顺序容器类型 介绍
vector 可变大小数组。支持快速随机访问。在尾部之外的位置 插入或删除元素可能很慢。
deque 双端队列。支持快速随机访问。在头尾位置 插入/删除速度很快。
list 双向链表。只支持双向顺序访问。在list中任何位置 进行插入/删除操作速度都很快。
forward_list 单向链表。只支持单向顺序访问。在链表任何位置 进行插入/删除操作速度都很快。
array 固定大小数组。支持快速随机访问。不能添加或者删除元素。
string 与vector相似的容器,但专门用于保存字符。随机访问块。在尾部插入/删除速度快。
建议:现代C++程序应该使用 标准库容器,而不是更原始的数据结构,如内置数组。

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
	}
}

(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) 容器操作可能使迭代器失效:
在向容器中添加元素后:
  • 如果容器是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操作

(1) 构造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个字符的拷贝。
注:n、len2、pos2都是无符号值。
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!!!"
}

(2) substr操作:
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可以使任何一种顺序容器以栈的方式工作)
除了顺序容器外,标准库还定义了三个顺序容器适配器:stackqueue、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构造