1.1. 容器的概观和分类
容器分为两种:序列式容器和关联式容器。
1.1.1. 序列式容器
序列式容器是指容器中的元素都可序( ordered ),但未必有序( sorted )。序列式容器通常是一个半开区间 [first, last) , 即 last 指向最后一个元素的下一个, last-first 为容器大小。
1.2. vector
1.2.1. vector 定义摘要
1.2.2. vector 迭代器
vector 迭代器是一个 random access vector ,且 vector 是一个连续存储空间,普通指针就能满足,因此 vector 迭代器就是一个普通指针。
1.2.3. vector 数据结构
vector 是一个连续存储空间,三个迭代器 start 、 finish 和 out_of_storage 能标明 vector 的大小、容量,大小是允许客户访问的区间,或者说是客户申请的空间,容量大于或等于大小。
1.2.4. vector 的构造与内存管理: constructor 和 push_back
push_back 时无备用空间( finish==out_of_storage )时调用 insert_aux 。 insert_aux 完成 position 位置插入,当无备用空间时,申请当前容量的 2 倍为新的空间,若原容量为 0 则,新空间大小为 1 (似乎都喜欢采用原空间的 2 倍及以上来扩容,例如内存池)。
1.2.5. vector 的元素操作: pop_back, erase, clear, insert
vector 元素操作的原则是能不调用构造函数的绝不调用!(例如复制一个元素到一个正在用的空间就不用再调用构造函数)。
1.3. list
1.3.1. list 概述
和 vector 相比, list 插入和移除元素的更高效,但 list 随机访问更低效,可参考 [Lippman98]6.3 节对这两种容器的一份测试报告。
1.3.2. list 的节点( node )
list 的节点支持双向移动。
1.3.3. list 的迭代器
list 的迭代器必须具备递增、递减、取值、成员存取等操作,是个 bidirectional iterator 。 list 迭代器其实是指向节点的指针。
1.3.4. list 的数据结构
list 是一个双向链表,其链尾是一个空白节点,只有一个迭代器,当其指向空白节点时为 last 迭代器。 begin 、 end 、 empty 和 size 函数均由此迭代器实现。
1.3.5. list 的元素操作
双向循环链表的操作如下(单个迭代器指向尾部的空节点):
1) erase 操作:删除节点的前节点的后节点迭代器指向删除节点的后节点,删除节点的后节点的前节点迭代器指向删除节点的前节点,然后删除节点。
2) unique 操作(移出连续且相同的元素): first 迭代器和 last 迭代器分别指向链表的头尾,遍历迭代器 next 指向 first ;移动 next ,当 next 和 first 所指对象的值相同则删除;否则调整范围,使迭代器 first 指向 next ;当 next 等于 last 时算法停止。算法需要注意的是每次循环始终比较 first 和 first 之后的节点是否相等效率最高,即每次循序开始后移 next 并判断是否等于 last ,每次循环结束时 next 等于 first 。
3) tranfer 操作(即将 [first, last )内的所有元素移动到 position 之前):先将正向链断开,链接到目标位置,然后将反向链断开,链接到目标位置。
void splice ( iterator position , list & /* 为何?为了客户调用方便? */ , iterator i )
{
iterator j = i ;
++ j ;
if ( position == i || position == j ) return ; // 若 i 指向的节点是 position 或
// position 前位置没有必要移动
transfer ( position , i , j );
}
merge 操作(合并两个递增有序的链表,合并后仍保持递增): [first1, last1) 和 [first2, last2) 分别指向目标链表和源链表中未合并的节点范围;移动 first1 和 first2 ,循环合并两个链表,依次将源链表中的节点有序的插入到目标链表中;循环结束后,若源链表中仍有节点时,则插入到目标链表的尾部。