vector 提供的是Random Access Iterators ,迭代器具备的操作有operator*,operator->,operator++,operator–,operator+, operator-,
operator+=,operator-=
template T, classAlloc = alloc>
class vector {
//...
protected:
typedef simple_allocdata_allocator;
};
配置n个元素的空间
data_allocator::allocate(n)
当在vector安插新元素时,如果capacity足够大,直接挑中finish迭代器完成安插,
否则则扩充从一块新空间
新空间并非从原来基础上扩充,而是配置一块新的较大的空间(大小为原来2倍),将原来内容拷贝过来。一旦引起新空间配置,原有迭代器将会失效。
vector部分元素操作
iterator erase(iterator position)
{
if (position + 1 != end())
copy(position + 1, finish, position);
--finish;
destroy(finish);
return position;
}
iterator erase(iterator first, iterator last)
{
iterator i = copy(last, finish, first);
// 析构掉需要析构的元素
destroy(i, finish);
finish = finish - (last - first);
return first;
}
其中copy的源码如下
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
start- - -first- - - last - - - - - finish - - -end_of_storage
start- - -first(last)- - - - -(finish) - - -finish- - -end_of_storage
erase(first,last) 过程为将last~finish段复制到 first~first+(finish-last)之中,并释放掉first+(finish-last)~finish的值
2.insert
template <class T, class Alloc>
void insert(iterator position, size_type n, const T& x)
{
// 如果n为0则不进行任何操作
if (n != 0)
{
if (size_type(end_of_storage - finish) >= n)
{ // 剩下的备用空间大于等于“新增元素的个数”
T x_copy = x;
// 以下计算插入点之后的现有元素个数
const size_type elems_after = finish - position;
iterator old_finish = finish;
if (elems_after > n)
{
// 插入点之后的现有元素个数 大于 新增元素个数
uninitialized_copy(finish - n, finish, finish);
finish += n; // 将vector 尾端标记后移
copy_backward(position, old_finish - n, old_finish);
fill(position, position + n, x_copy); // 从插入点开始填入新值
}
else
{
// 插入点之后的现有元素个数 小于等于 新增元素个数
uninitialized_fill_n(finish, n - elems_after, x_copy);
finish += n - elems_after;
uninitialized_copy(position, old_finish, finish);
finish += elems_after;
fill(position, old_finish, x_copy);
}
}
else
{ // 剩下的备用空间小于“新增元素个数”(那就必须配置额外的内存)
// 首先决定新长度:就长度的两倍 , 或旧长度+新增元素个数
const size_type old_size = size();
const size_type len = old_size + max(old_size, n);
// 以下配置新的vector空间
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
__STL_TRY
{
// 以下首先将旧的vector的插入点之前的元素复制到新空间
new_finish = uninitialized_copy(start, position, new_start);
// 以下再将新增元素(初值皆为n)填入新空间
new_finish = uninitialized_fill_n(new_finish, n, x);
// 以下再将旧vector的插入点之后的元素复制到新空间
new_finish = uninitialized_copy(position, finish, new_finish);
}
# ifdef __STL_USE_EXCEPTIONS
catch(...)
{
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
destroy(start, finish);
deallocate();
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
}
};
// 以下首先将旧的vector的插入点之前的元素复制到新空间
new_finish = uninitialized_copy(start, position, new_start);
// 以下再将新增元素(初值皆为n)填入新空间
new_finish = uninitialized_fill_n(new_finish, n, x);
// 以下再将旧vector的插入点之后的元素复制到新空间
new_finish = uninitialized_copy(position, finish, new_finish);
double linked-list 链式数据结构的优势 ,具有效率较高的插入及删除效率
template <class T>
struct__list_node {
typedef void* void_pointer;
void_pointer prev; //型别为 void*。其实可设为 __list_node*
void_pointer next;
Tdata;
};
list迭代器
迭代器具备具备前移后移的能力 Bidirectional Iterators
operator*,operator->,operator++,operator–, operator+=,operator-=
list构建与内存管理
与vector类似,预设使用alloc,并通过list_node_allocator配置空间
template T, classAlloc = alloc>// 预设使用 alloc 为配置器
class list {
protected:
typedef __list_node<T> list_node;
typedef simple_alloclist_node_allocator;
...
};
配置n个节点的空间
list_node_allocator(n)
creat_node配置新节点,用于安插,对空间配置相较vector简单。
link_type create_node(const T& x) {
link_type p =get_node();
construct(&p->data, x); //全域函式,建构/解构基ᴀ工具。
return p;
}
void transfer(iterator position, iterator first, iterator last) {
if (position != last) {
(*(link_type((*last.node).prev))).next = position.node;
(*(link_type((*first.node).prev))).next = last.node;
(*(link_type((*position.node).prev))).next = first.node;
link_type tmp = link_type((*position.node).prev);
(*position.node).prev = (*last.node).prev;
(*last.node).prev = (*first.node).prev;
(*first.node).prev = tmp;
}
}
双向开口的连续线性空间
deque与vector
deque中控器
采用一小块连续的空间(map),其中每一个节点指向较大的线性连续空间(缓冲区),即deque存储空间主体,SGI STL允许指定缓冲区大小
template T, classAlloc = alloc, size_t BufSiz = 0>
class deque {
public: // Basic types
typedef Tvalue_type;
typedef value_type*pointer;
...
protected: // Internal typedefs
// 元素的指针的指针( pointer of pointer of T)
typedef pointer* map_pointer;
protected: // Data members
map_pointermap; //指向 map, map 是块连续空间,其内的每个元素
// 都是一个指标(称为节点), 指向一块缓冲区。
size_typemap_size; // map 内可容纳多少指标。
...
};
deque迭代器需要知道自己所在的缓冲区位置,以及能够区分是否处在缓冲区域边缘,来确定是否要跳跃到上/下 个缓冲区域
start 和finish迭代器指向第一个缓冲区和最后一个缓冲区,start.first前和finish.last后均有一定备用空间
跳跃函数void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + difference_type(buffer_size());
}
operator++和operator--判断cur与first/last的关系 决定是否调用set_node
T* cur; //此迭代器所指之缓冲区中的现行(current)元素
T* first; //此迭代器所指之缓冲区的头
T* last; //此迭代器所指之缓冲区的尾(含备用空间)
map_pointer node; //指向管控中心
deque 的数据结构
即map,缓冲区,start与finsh两个迭代器
map为块连续空间,当map不够时候,重新开辟新的一块
deque 的建构与内存管理
预设使用alloc 并通过data_allocator和map_allocator分配空间
protected: // Internal typedefs
// 专属之空间配置器,每次配置一个元素大小
typedef simple_alloc data_allocator;
// 专属之空间配置器,每次配置一个指针大小
typedef simple_alloc map_allocator;
constructor如下
deque(int n, const value_type& value)
: start(), finish(), map(0), map_size(0)
{
fill_initialize(n, value);
}
fill_initialize 内部调用create_map_and_nodes()负责产生并安排好deque的结构。并且该函数(f…)完成初始化
push_back如下
void push_back(const value_type& t) {
if (finish.cur != finish.last - 1)
// 最后缓冲区尚有一个以上的备用空间
construct(finish.cur, t); //直接在备用空间上建构元素
++finish.cur; //调整最后缓冲区的使用状态
}
else // 最后缓冲区已无(或只剩一个)元素备用空间。
push_back_aux(t);
}
其需要完成内容
1确定是否更换map若是并执行之通过(push_back_aux所调用的函数 reserve_map_ai_back所为)
void reserve_map_at_back (size_type nodes_to_add = 1) {
if (nodes_to_add + 1 > map_size - (finish.node - map))
// 如果 map尾端的节点备用空间不足
// 符合以上条件则必须重换一个 map(配置更大的,拷贝原来的,释放原来的)
reallocate_map(nodes_to_add, false);
}
2改变finish迭代器指向的位置,调整cur
push_front语义与之类似
同样,在执行pop相关操作时候,也会根据相应条件选择释放缓冲区
push_front(front())
容器前push一个与front相同的节点,然后通过
front2(old)~pos1 向前搬移一位至front1(new)~pos1-1然后插入到pos中
copy(front2, pos1, front1);
搬移节点。后方元素少于此相同。
heap是通过vector实现的一个堆,并不归属STL容器组件,作为priority queue的底层机制
本质就是堆(完全二叉树)。
通过数组表示树i子节点为2*i 和2*i+1,在排序算法中都有堆及堆排序,并不赘述。以下函数是STL实现的堆排序算法
描述一下heap所提供的函数(以max-heap 大根堆 来解释)
void__push_heap(RandomAccessIterator first, Distance holeIndex,
Distance topIndex, T value) {
Distance parent = (holeIndex - 1) / 2; //找出父节点
while (holeIndex > topIndex && *(first + parent) < value) {
// 当尚未到达顶端,且父节点小于新值(于是不符合 heap 的次序特性)
// 由于以上使用 operator<,可知 STL heap 是一种 max-heap(大者为父)。
*(first + holeIndex) = *(first + parent); //令洞值为父值
holeIndex = parent; //percolate up:调整洞号,向上提升至父节点。
parent = (holeIndex - 1) / 2; //新洞的父节点
} // 持续至顶端,或满足 heap 的次序特性为止。
*(first + holeIndex) = value; //令洞值为新值,完成安插动作。
}
此函数前提 须是新元素已经push入在vector尾部,用于调整该尾部元素位置,插入堆中合适位置
pop_heap()
及首部元素(根)至队尾,并在内部调用__adjust_heap调整n-1的元素为堆。
sort_heap()即堆排序
void sort_heap(RandomAccessIterator first,
RandomAccessIterator last) {
// 以下,每执行一次 pop_heap() ,极值(在 STL heap 中为极大值)即被放在尾端。
// 扣除尾端再执行一次 pop_heap() ,次极值又被放在新尾端。 一直下去,最后即得
// 排序结果。
while (last - first > 1)
pop_heap(first, last--); //每执行 pop_heap() 一次,操作范围即退缩一格。
}
template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first,
RandomAccessIterator last) {
__make_heap(first, last, value_type(first), distance_type(first) );
}
//以下这组 make_heap() 不允许指定「大小比较标准」。
template <class RandomAccessIterator, class T, class Distance>
void__make_heap(RandomAccessIterator first,
RandomAccessIterator last, T*,
Distance*) {
if (last - first < 2) return; //如果长度为 0或 1,不必重新排列。
Distance len = last - first;
// 找出第一个需要重排的子树头部,以 parent 标示出。由于任何叶节点都不需执行
// perlocate down,所以有以下计算。 parent命名不佳,名为 holeIndex更好。
Distance parent = (len - 2)/2;
while (true) {
// 重排以 parent 为首的子树。 len 是为了让 __adjust_heap() 判断操作范围
__adjust_heap(first, parent, len, T(*(first + parent)));
if (parent == 0) return; //走完根节点,就结束。
parent--;
}
}
即将现有数据转换为堆
用于权值的queue,利用max-heap完成。
构建时候调用make_heap,添加元素(push)时候使用用push_heap,
弹出元素pop使用pop_heap(只是放在容器尾部)+底层容器的pop_back实现弹出
slist 单向链表,迭代器为单也 Forward Iterator
其节点及迭代器架构如下图
//单向串行的节点基ᴀ结构
struct__slist_node_base
{
__slist_node_base* next;
};
//单向串行的节点结构
template <class T>
struct__slist_node : public __slist_node_base
{
T data;
};