4.顺序容器
1.vector
- 比较简单,连续的空间,不够就申请
size()+max(size(), 新增的n)
的内存,释放原先的内存,(所谓的vector原有迭代器可能会失效的问题),略- 可以直接使用指针作为迭代器
- 基本的iterator_type如下
template <typename T, typename Alloc=alloc>
class vector{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type* iterator; //主要是内部调用,对于iterator_traits,已经对指针做了特化
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
};
复制代码
2. list
list实际是一个双向链表
// list_node 的实现
template <class T>
struct __list_node {
typedef void* void_pointer; //写成__list_node<T>* 更为准确
void_pointer prev;
void_pointer next;
T data;
};
复制代码
- list不能像vector以普通指针作为迭代器,因为不保证在存储空间连续,需要自己实现迭代器
template<typename T, typename Ref, typename Ptr>
struct __list_iterator {
public:
typedef __list_iterator<T, T&, T*> iterator; //不清楚Ref和Ptr的使用情景
typedef __list_iterator<T, Ref, Ptr> self;
typedef bidirectional_iterator_tag iterator_category; //双向迭代器
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
link_type node;
__list_iterator(link_type x) :node(x) {}
__list_iterator() {}
__list_iterator(const iterator& x): node(x.node) {}
bool operator == (const self& x) const {return node == x.node;}
bool operator != (const self& x) const {return node != x.node;}
reference operator*() const {return (*node).data;}
pointer operator->() const {return &(operator*());}
self& operator++() {
node = (link_type) ((*node).next);
return *this;
}
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
self& operator--() {
node = (link_type) ((*node).prev);
return *this;
}
self operator--(int) {
self tmp = *this;
++*this;
return tmp;
}
};
复制代码
- 而对于list的实现,实际只需要存储一个
__list_node
的指针即list::node,指向为end()返回的迭代器对应的node- 空列表的node->next==node->prev==node
- 大部分方法是基于一些基本方法的封装,如erase, insert,简单的修改node的prev和next来实现,
- 还有个比较常用的是transfer,splice等是基于transfer
- list的sort需要单独实现因为STL sort算法只接受RamdonAccessIterator
template<class T, class Alloc=alloc>
class list{
protected:
typedef __list_node<T> list_node;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
public:
typedef list_node* link_type;
typedef __list_iterator<T, T&, T*> iterator;
protected:
link_type node; //为尾端节点,next指向第一个
link_type get_node() {return list_node_allocator::allocate();}
void empty_initialize() {
node = get_node();
node->next = node;
node->prev = node;
}
public:
iterator begin() {return (link_type)(node->next);} //因为__list_iterator有对应的非explict 构造函数
iterator end() {return node;}
protected:
// 移动迭代范围内的元素到position之前
void transfer (iterator position, iterator first, iterator last) {
if (position != last) {
link_type tmp = position.node->prev;
last.node->prev->next = position.node;
first.node->prev->next = last.node;
position.node->prev->next = first.node;
position.node->prev = last.node->prev;
last.node->prev = first.node->prev;
first.node->prev = tmp;
}
}
};
复制代码
3.deque
deque
和vector
类似,只是双向开口,和支持O(1)的时间对头部进行插入移除deque
动态的以分段连续空间组成,不存在vector上的复制元素到新空间再删除旧空间的操作deque
需要自己实现迭代器,因为实际非连续空间,其效率较vector较低,建议尽量用vector,一些类似排序的操作可以将deque复制到vector等操作完成后再复制回来
3.1 deque
的中控器
- deque最核心的部分是在分段的定量连续空间,维护其整体连续的假象,这部分通过map实现
- map是一块连续空间,每个元素指向另一段较大的连续空间即buffer,被指向的连续空间是deque的存储主体
- map满载会类似vector重新找一块空间
deque迭代器的实现,重点是是 operate
+-相关方法的重载,buffer_size是缓冲区的大小
#ifndef STL_DEQUE
#define STL_DEQUE
#include "../allocator/stl_alloc.h"
#include "../allocator/stl_construct.h"
inline size_t __deque_buf_size(size_t n, size_t sz){
return n !=0 ? n:(sz < 512 ? size_t(512/sz): size_t(1));
}
template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator{
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
typedef __deque_iterator<T, const T&,const T*, BufSiz> const_iterator;
static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T));}
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T** map_pointer;
typedef __deque_iterator self;
T* cur;
T* first;
T* last;
map_pointer node;
void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + difference_type(buffer_size());
}
reference operator*() const {return *cur;}
pointer operator->() const {return &(operator*());}
difference_type operator-(const self& x) const {
return difference_type(buffer_size()) * (node-x.node-1) + (cur-first) + (x.last-x.cur);
}
self& operator++(){
++cur;
if (cur==last) {
set_node(node+1);
cur=first;
}
return *this;
}
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
self& operator--(){
if (cur==first){
set_node(node-1);
cur=last;
}
--cur;
return *this;
}
self operator--(int){
self tmp=*this;
--this;
return tmp;
}
//略
};
#endif
复制代码
关于deque的实现,重点在与内存的分配,即map的增减。
- 当push_front或者push_end时map有空余node时,正常操作
- 否则当map_size 大于2倍的需要的nodes数时,简单的移动map内的数据。
- 否则重新分配map。
- deque还提供了
insert方法
,实现的原理是比较position前后数数据那边少,如果前面少,就push_front第一个元素,然后移动前面的数据。
template<typename T, class Alloc=alloc, size_t BufSiz=0>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
protected:
typedef pointer* map_pointer;
protected:
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
protected:
typedef simple_alloc<value_type, Alloc> data_allocator;
typedef simple_alloc<pointer, Alloc> map_allocator;
public:
size_type buffer_size() {return __deque_buf_size(BufSiz, sizeof(value_type));}
deque(int n, const value_type& value): start(), finish(), map(0), map_size(0) {fill_initialize(n, value);}
void fill_initialize(size_type n, const value_type& value){
create_map_and_nodes(n);
map_pointer cur;
for (cur=start.node; cur<finish.node;++cur){
uninitialized_fill(*cur, *cur+buffer_size(), value);
}
uninitialized_fill(finish.start, finish.cur, value);
}
void create_map_and_nodes(size_type num_elements){
size_type num_nodes = num_elements / buffer_size()+1;
map_size = max(initial_map_size(), num_nodes + 2); // 为8和node数+2的较大值,保证map前后各预留一个
map = map_allocator::map_allocate(map_size);
map_pointer nstart = map + (map_size-num_nodes) / 2;
map_pointer nfinish = nstart + num_nodes - 1;
map_pointer cur;
for (cur=nstart; cur<=nfinish; ++cur) *cur=allocate_node();
start.set_node(nstart);
finish.set_node(nfinish);
start.cur = start.first;
finish.cur = finish.first + num_elements % buffer_size();
}
void push_back(const value_type& t){
if (finish.cur!=finish.last-1){ //无需切换node
construct(first.cur, t);
++finish.cur;
}
else{
push_back_aus(t);
}
}
void push_back_aus(const value_type& t){
value_type t_copy=t;
reserve_map_at_back();
*(finish.node+1) = allocate_node();
__STL_TRY{
construct(first.cur, t_copy);
finish.set_node(finish.node+1);
finish.cur=finish.first;
}
__STL_UNWIND(deallocate_node(*(finish.node+1)));
}
void reverse_map_at_back(size_type nodes_to_add=1){
if(nodes_to_add+1>map_size - (finish.node-map)) reallocate_map(nodes_to_add, false); //尾端节点不够,调整map
}
void reallocate_map(size_type nodes_to_add, bool add_at_front){
size_type old_num_nodes = finish.node-start.node+1;
size_type new_num_nodes = old_num_nodes + nodes_to_add;
map_pointer new_nstart;
if (map_size > 2*new_num_nodes) {
new_nstart=map+(map_size-new_num_nodes) / 2+(add_at_front?nodes_to_add:0);
if (new_nstart < start.node){
copy(start.node, finish.node+1, new_nstart);
} else {
copy_backward(start.node, finish.node+1, new_nstart+old_num_nodes);
}
} else {
size_type new_map_size=map_size+max(map_size, nodes_to_add) + 2;
map_pointer new_map = map_allocator::allcocate(new_map_size);
new_nstart = new_map + (new_map_size-new_num_nodes) /2 + (add_at_front?nodes_to_add:0);
copy(start.node, finish.node+1, new_nstart);
map_allocator::deallocate(map, map_size);
map = new_map;
map_size = new_map_size;
}
start.set_node(new_nstart);
finish.set_node(new_nstart+old_num_nodes-1);
}
};
复制代码
4. stack
和queue
- 从实现上的方式上看实际都是
adapter
(配接器),如list
和deque
已经提供了满足操作的数据结构- STL默认将
deque
作为stack
和queue
的底层容器stack
和queue
没有迭代器
template<class T, class Sequence=deque<T>>
class stack{
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_referenc;
protected:
Sequence c;
public:
bool empty() const {return c.empty();}
};
复制代码
5 heap
和priority_queue
priority_queue
支持以任意顺序将元素推入容器,但是取出是按照优先级取出来heap
是priority_queue
配接器一个底层的容器,是一个max_heap完全二叉树vector
是heap
的默认底层容器- 可以用
array
存储树的节点,array[0]位置保留,则,a[i]的左子节点为a[2i],右子节点为a[2i+1]
//first和last必须为vector的头尾, index0没有空着
template<class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last){
__pop_heap_aux(first, last, value_type(first));
}
template<class RandomAccessIterator, class Distance, class T>
inline void __pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*){
__pop_heap(first, Distance(last-first-1), Distance(0), T(*(last-1)));
}
template<class RandomAccessIterator, class Distance, class T>
inline void __pop_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value){
Distance parent = (holeIndex -1) / 2;
while(holeIndex>topIndex && *(first+parent) < value){
*(first+holeIndex) = *(first+parent);
holeIndex = parent;
parent = (holeIndex-1)/2;
}
*(first+holeIndex)=value;
}
#endif
// pop类似,是将最后一个元素放到第一个,然后,由根开始替换,但是会把首元素不去除而是放到vector结尾
// sort方法会调用pop,每次last减1
复制代码