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;
};
複製代碼
list
list實際是一個雙向鏈表node
// 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;
}
}
};
複製代碼
deque
deque
和vector
相似,只是雙向開口,和支持O(1)的時間對頭部進行插入移除deque
動態的以分段連續空間組成,不存在vector上的複製元素到新空間再刪除舊空間的操做deque
須要本身實現迭代器,由於實際非連續空間,其效率較vector較低,建議儘可能用vector,一些相似排序的操做能夠將deque複製到vector等操做完成後再複製回來
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的增減。c++
- 當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);
}
};
複製代碼
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();}
};
複製代碼
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
複製代碼