顺序容器
容器库概览
迭代器
与容器一样,迭代器有着公共的接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的。
迭代器范围
一个迭代器范围是由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者尾元素之后的位置。这两个迭代器通常被称为 begin 和 end,或者 first 和 last
这种元素范围被称为左闭合区间 [begin, end)
使用左闭合范围的编程假定
容器类型成员
反向迭代器是一种反向遍历容器的迭代器,与正向迭代器相比,各种操作的含义都发生了跌倒
例如:对一个反向迭代器执行++操作,会得到上一个元素
begin 和 end 成员
带 r 的版本返回反向迭代器,以 c 开头的版本返回 const 迭代器
与 const 指针和引用类似,可以将一个普通的 iterator 转换成对应的 const_iterator,但反之则不行
当不需要写访问时,应使用 cbegin 和 cend
容器定义和初始化
每个容器类型都定义了一个默认构造函数。除 array 之外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数
将一个容器初始化为另一个容器的拷贝
直接拷贝整个容器,或者(array除外)拷贝由一个迭代器对指定的元素范围
标准库 array 具有固定大小
由于大小是 array 类型的一部分,array 不支持普通的容器构造函数。这些构造函数都会确定容器的大小,隐式或显式
一个默认构造的 array 是非空的,它包含了与其大小一样多的元素。这些元素都被默认初始化,就像一个内置数组中的元素那样
赋值和 swap
赋值运算符将其左边容器中的全部元素替换成右边容器中元素的拷贝
第一个赋值运算后,左边容器将于右边容器相等。如果两个容器原来大小不同,赋值运算后两者的大小都与右边容器的大小相同。第二个赋值运算后,c1 的 size 变成 3
与内置数组不同,标准库 array 类型允许赋值。赋值号左右两边的运算对象必须具有相同的类型
由于右边运算对象的大小可能与左边运算对象的大小不同,因此 array 类型不支持 assign,也不允许用花括号包围的值列表进行赋值
使用 assign (仅顺序容器)
赋值运算符要求左边和右边的运算对象具有相同的类型。它将右边运算对象中所有元素拷贝到左边运算对象中。顺序容器(array 除外)还定义了一个 assign 的成员,允许我们从一个不同但是相容的类型赋值,或者从容器的一个子序列赋值。assign 操作用参数指定的元素(的拷贝)替换左边容器中的所有元素。
使用 swap
swap 操作交换两个相同类型容器的内容。调用 swap 之后,两个容器中的元素将会交换:
除 array 外,swap 不对任何元素进行拷贝,删除或插入操作,因此可以保证在常数时间内完成
元素不会移动意味着,除 string 外,指向容器的迭代器,引用和指针在 swap 操作之后都不会失效。它们仍指向 swap 操作之前所指向的那些元素。但是,在 swap 之后,这些元素已经属于不同的容器了
与其他容器不同,swap 两个 array 会真正交换它们的元素,因此,交换两个 array 所需的时间与 array中元素的数目成正比
容器大小操作
关系运算符
比较两个容器实际上是进行元素的逐对比较,这些运算符的工作方式与 string 的关系运算符类似
容器的关系运算符使用元素的关系运算符完成比较
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器
顺序容器操作
向顺序容器添加元素
不同容器使用不同的策略来分配元素空间,而这些策略会影响性能。在一个 vector 或 string 的尾部之外的任何位置,或是 deque 的首位之外的任何位置添加元素,都需要移动元素。而且,向一个 vector 或 string 添加元素可能引起整个对象存储空间的重新分配
使用 push_back
使用 push_front
除了 push_back,list,forward_list 和 deque 容器还支持名为 push_front 的类似操作,此操作将元素插入到容器头部
在容器中的特定位置添加元素
insert 允许我们在任意位置插入0个或者多个元素,vector,deque,list 和 string 都支持 insert 成员
insert 函数将元素插入到迭代器所指定的位置之前
插入范围内元素
insert 函数还可以接受更多的参数
比如接受一个元素数目和一个值,它将指定数量的元素添加到指定位置之前,这些元素都按给定值初始化
使用 insert 的返回值
使用 emplace 操作
emplace_front,emplace 和 emplace_back,这些操作构造而不是拷贝元素,这些操作分别对应 push_front,insert,push_back
当调用 push 或 insert 成员函数时,我们将元素类型的对象传递给他们,这些对象被拷贝到容器中。当我们调用一个 emplace 成员函数时,则是将参数传递给元素类型的构造函数。emplace 成员使用这些参数在容器管理的内存空间中直接构造元素
// iter 指向 c 中一个元素,其中保存了 Sales_data 元素
访问元素
获得的是 c 中首元素和尾元素的引用
在调用 front 和 back(或解引用 begin 和 end 返回的迭代器)之前,要确保 c 非空
访问成员函数返回的是引用
在容器中访问元素的成员函数(即,front,back,下标 和 at )返回的都是引用。如果容器是一个 const 对象,则返回值为 const 的引用。否则就是一个普通引用
删除元素
pop_front 和 pop_back 成员函数
从容器内部删除一个元素
成员函数 erase 从容器中指定位置删除元素。我们可以删除由一个迭代器指定的单个元素,也可以删除由一个迭代器指定的范围内的所有元素。两种形式的 erase 都返回指向删除的(最后一个)元素之后位置的迭代器。
若 j 是 i 之后的元素,那么 erase(i)将返回指向 j 的迭代器
删除多个元素
接受一对迭代器的 erase 版本允许我们删除一个范围内的元素:
改变容器的大小
可以用 resize 来增大或缩小容器。array 不支持 resize。如果当前大小大于所要求的大小,容器后部的元素会被删除,如果当前大小小于新大小,会将新元素添加到容器后部
resize 操作接受一个可选的元素值参数,用来初始化添加到容器中的元素。如果调用者未提供此参数,新元素进行值初始化。如果容器保存的是类类型元素,且 resize 向容器添加新元素,则我们必须提供初始值,或者元素类型必须提供默认构造函数
容器操作可能使迭代器失效
向容器中添加元素或从容器中删除元素的操作可能会使指向容器元素的指针,引用或迭代器失效
编写改变容器的循环程序
添加/删除 vector,string 或 deque 元素的循环程序必须考虑迭代器,引用和指针可能失效的问题。程序必须保证每个循环步中都更新迭代器,引用或指针。如果循环中调用的是 insert 或 erase,那么更新迭代器很容易,这些操作都返回迭代器,我们可以用来更新
// insert 在给定位置之前插入新元素,然后返回指向新插入元素的迭代器
不要保存 end 返回的迭代器,当我们添加/删除 vector 或 string 的元素后,或在 deque 中首元素之外任何位置 添加/删除 元素后,原来end返回的迭代器总会失效
必须在每次插入操作后重新调用 end( ),而不能在循环开始前保存它返回的迭代器