STL源碼剖析-4

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實際是一個雙向鏈表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;
			}
		}
};
複製代碼

3.deque

  • dequevector相似,只是雙向開口,和支持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的增減。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);
		}
};
複製代碼

4. stackqueue

  • 從實現上的方式上看實際都是adapter(配接器),如listdeque已經提供了知足操做的數據結構
  • STL默認將deque做爲stackqueue的底層容器
  • stackqueue沒有迭代器
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 heappriority_queue

  • priority_queue支持以任意順序將元素推入容器,可是取出是按照優先級取出來
  • heappriority_queue配接器一個底層的容器,是一個max_heap徹底二叉樹
  • vectorheap的默認底層容器
  • 能夠用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
複製代碼
相關文章
相關標籤/搜索