第二部分 容器和算法
第九章 顺序容器
顺序容器、关联容器 只是逻辑上的,物理上的存储有 顺序存储、链式存储,容器使用哪种存储方式,根据具体需要。
顺序容器,是将 单一类型元素 聚集起来成为容器,然后 根据位置 来存储和访问这些元素,标准库定义了三种顺序容器类型:vector(支持快速随机访问)、list(支持快速插入/删除)、deque(double-ended queue的简写,双端队列),它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价,deque 是一种具有队列和栈的性质的数据结构,双端队列中的元素可以从两端弹出,相比list增加 [] 运算符重载(百度百科)
容器类共享公共的接口,容器只定义了少量的操作,大多数额外操作则由算法库提供;容器类型的操作集合:一些操作适用于所有容器类型,另外一些操作则只适用于顺序或关联容器类型,还有一些操作只适用于顺序或关联容器类型的一个子集
9.1 顺序容器的定义
所有的容器都是类模板,所有的容器类型都定义了默认构造函数,用于创建指定类型的空容器对象
9.1.1 容器元素的初始化
除了默认构造,容器类型还提供了其他的构造函数,使程序员可以指定元素的初值
将一个容器初始化为另一个容器的副本:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同;
初始化一段元素的副本:尽管不能直接将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器间接实现该功能;迭代器不要求容器类型相同,容器内的元素类型也可以不同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制,迭代器标记了要复制的元素范围,这些元素用于初始化新容器的元素,采用这种初始化形式可复制不能直接复制的容器,更重要的是,可以实现复制其他容器的一个子序列
分配和初始化指定数目的元素:创建容器时,可显式指定容器的大小和一个(可选的)元素初始化式,不提供元素初始化式时,标准库将为容器实现值初始化,接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化
9.1.2 容器内元素的类型约束
容器元素类型必须满足以下两个约束:元素类型必须支持赋值运算,元素类型的对象必须可以复制 (引用不支持一般意义的赋值运算,IO库类型不支持复制或赋值),此外,一些容器操作对元素类型还有特殊要求
可以定义元素是容器类型的容器( 在指定容器元素为容器类型时,必须用空格隔开两个相邻的 > 符号,以示这是两个分开的符号)
9.2 迭代器和迭代器范围
与容器类型一样,所有迭代器具有相同的接口:如果某种迭代器支持某种操作,那么支持这种操作的其他迭代器也会以相同的方式支持这种操作( * 、-> 、 ++ 、 -- 、==、 != ),只有vector和deque容器提供下面两种重要的运算集合:迭代器算术运算,以及 使用除了==和!=之外的关系操作符来比较两个迭代器 (==和!=适用于所有容器)
9.2.1 迭代器范围
迭代器范围这个概念是标准库的基础,C++语言 使用一对迭代器 标记迭代器范围,这两个迭代器分别指向同一个容器中的两个元素或超出末端的下一个位置,用于标记容器中的一段元素范围
9.2.2 使迭代器失效的容器操作
一些容器操作(元素添加或删除)会 修改容器的内在状态或移动容器内的元素,这样的操作使所有指向被移动的元素的迭代器失效,也可能同时使其他迭代器失效
9.3 顺序容器的操作
9.3.1 容器定义的类型别名
由容器定义的类型:size_type、 iterator 、const_iterator、reverse_iterator、const_reverse_iterator、difference_type 、value_type、reference、const_reference,最后三种类型使程序员无须直接知道容器元素的真正类型,就能使用它,需要使用元素类型时,只要用value_type即可,如果引用该类型,则通过reference和const_reference类型实现
9.3.2 begin和end成员
begin和end操作产生指向容器内的第一个元素和最后一个元素的下一个位置的迭代器(rbegin、rend操作返回 逆向迭代器),迭代器是否为const取决于容器是否为const
9.3.3 在顺序容器中添加元素
不要存储end操作返回的迭代器,添加或删除deque或vector容器内的元素都会导致存储的迭代器失效
9.3.4 关系操作符
C++语言只允许两个容器做 其元素类型定义的 关系运算
9.3.5 容器大小的操作
size,max_size,empty,resize
resize操作可能会使迭代器失效,在vector或deque容器上做resize操作有可能会使其所有的迭代器都失效,对于所有的容器类型,如果resize操作压缩了容器,则指向以删除的元素的迭代器失效
9.3.6 访问元素
如果容器非空,那么容器类型的front和back成员将 返回容器内第一个或最后一个元素的 引用(间接的方法是begin或end迭代器),vector和deque容器 的 [] 下标操作符,返回下标元素的引用,at 返回下标元素的 引用(下标无效,at函数会抛出out_of_range异常)
9.3.7 删除元素
容器类型提供了通用的erase操作(返回迭代器,寻找一个指定元素的最简单的方法是使用标准库的find算法)和特定的pop_front、 pop_back操作(返回void,要获取删除的元素值,必须在删除元素之前调用front或back函数)来删除容器内的元素(vector容器类型不支持pop_front操作),clear操作删除所有元素(返回void,或将begin和end迭代器传递给erase )
9.3.8 赋值与swap
赋值和assign操作使左操作数容器的所有迭代器失效,swap操作则不会使迭代器失效,assign操作首先删除容器中的所有元素,然后将其参数所指定的新元素插入到该容器中,如果在不同(或相同)类型的容器内,元素类型不相同但是相互兼容,则其赋值运算必须使用assign函数
9.4 vector容器的自增长
为了使vector容器实现快速的内存分配,其实际分配的容量比当前所需的空间多一些,每当vector容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配,capacity操作 获取在容器需要分配更多的存储空间之前能够存储的元素总数,reserve操作告诉vector容器应该预留多少个元素的存储空间
9.5 容器的选用
选择容器类型的法则:如果程序要求随机访问元素,则应使用vector或deque容器;如果程序必须在容器中间插入或删除元素,则应采取list容器;如果程序不是在容器中间位置而是在容器首部或尾部插入或删除元素,则应采取deque容器;如果在需要在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个list容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的list容器复制到一个vector容器
9.6 再谈string类型
string类型有其特有的操作,还支持大多数顺序容器操作,可将string类型视为字符容器
9.6.1 构造string对象的其他方法
string不支持带有单个容器长度作为参数的构造函数,创建string对象时:可以不提供参数;初始化为另一个string对象的副本;用一对迭代器初始化;使用一个计数器和一个字符初始化
string构造函数的第一个参数:指向字符数组元素的指针,第二个参数标记复制多少个字符的计数器,数组则不必以空字符结束,用子串做初始化式:在创建string对象时,将其初始化为另一个string对象的子串
9.6.2 修改string对象的其他方法
string类型支持的许多容器操作在操作时都以迭代器为基础,insert(所有版本的insert函数的第一个参数都是一个指向插入位置之后的迭代器),assign,erase(erase操作需要一个迭代器或一段迭代器范围作其参数),string类型 和 容器类型的 共有的操作 以及 string类型 特有的操作(返回string的引用)
string类型提供本类型特有操作,基于位置 处理string对象,指定新的 内容:在string对象中insert或assign的字符可来自于字符数组或另一个string对象
9.6.3 只适合string类型的操作
string类型提供了容器类型不支持的其他几种操作:substr函数,返回当前string对象的子串;append和replace函数,用于修改string对象;一系列find函数,用于查找string对象
9.6.4 string类型的查找
string类提供了6种查找函数,每种函数以不同形式的find命名,每种查找操作都有4个重载版本,每个版本使用不同的参数集合(查找的到底是:单个字符、另一个string字符串、C风格的以空字符结束的字符串、用字符数组给出的特定数目的字符集合)
精确匹配的查找:最简单的查找操作是find函数,用于寻找实参指定的内容,如果找到的话,则返回第一次匹配的下标值,如果找不到,则返回string::npos(值-1);查找任意字符:查找字符串时,希望匹配任意指定的字符,find_first_of;指定查找的起点:可以给find操作传递一个可选的起点位置实参,用于指定开始查找的下标位置,该位置实参的默认值为0;寻找不匹配点:调用find_first_not_of函数查找第一个与实参不匹配的位置;反向查找:rfind成员函数;find_last函数:find_last_of、 find_last_not_of,都提供第二个实参,这个参数是可选的;
#include <iostream> #include <string> using namespace std; int main () { string num = "0123456789"; string name = "Hello8world9"; //匹配num字符串的任意字符 string::value_type pos = 0; while((pos = name.find_first_of(num, pos ) ) != string::npos ) { cout << (wchar_t)pos << "," << name[pos] << endl; //windows下pos不强转乱码 pos++; //下一个 } return 0; }
9.6.5 string对象的比较
string类型定义了所有关系操作符,可以比较两个对象是否相等、不等以及实现小于大于运算,string对象的比较与大小写敏感的字典顺序比较相同,string对象的整个比较依赖于不同字符之间的比较,除了关系操作符,string类型还提供了一组compare操作(返回正数、负数或0)
9.7 容器适配器
标准库还提供了三种顺序容器适配器:stack(后进先出)、queue(先进先出)和priority_queue(有优先级管理的队列)类型