1. 程式人生 > >STL 簡單 deque 的實現

STL 簡單 deque 的實現

Vector是單向開口的連續線性空間,而Deque是雙向開口的連續線性空間,可以在頭尾兩端插入和刪除元素,而且deque沒有容量(capacity)的概念,因為deque動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。

         Deque採用一塊map作為主控,這裡所謂map是一小塊連續空間,其中每個元素(此處稱為節點,node)都是指標,指向另一段連續線性空間,稱之為緩衝區。


deque的實現需要以下幾個檔案:

1.      globalConstruct.h,構造和解構函式檔案,位於//cghAllocator/

2.      cghAlloc.h,空間配置器檔案,位於/

/cghAllocator/

3.      cghDequeIterator.h,迭代器的實現,位於//cghDeque/

4.      cghDeque.h,cghDeque的實現,位於//cghDeque/

5.      test_cghDeque.cpp,測試程式碼,位於/test/

1.建構函式

先看第一個,globalConstruct.h建構函式檔案

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  
[email protected]
* * 功能:全域性構造和析構的實現程式碼 ******************************************************************/ #include "stdafx.h" #include <new.h> #include <type_traits> #ifndef _CGH_GLOBAL_CONSTRUCT_ #define _CGH_GLOBAL_CONSTRUCT_ namespace CGH { #pragma region 統一的構造解構函式 template<class T1, class T2> inline void construct(T1* p, const T2& value) { new (p)T1(value); } template<class T> inline void destroy(T* pointer) { pointer->~T(); } template<class ForwardIterator> inline void destroy(ForwardIterator first, ForwardIterator last) { // 本來在這裡要使用特性萃取機(traits程式設計技巧)判斷元素是否為non-trivial // non-trivial的元素可以直接釋放記憶體 // trivial的元素要做呼叫解構函式,然後釋放記憶體 for (; first < last; ++first) destroy(&*first); } #pragma endregion } #endif


按照STL的介面規範,正確的順序是先分配記憶體然後構造元素。建構函式的實現採用placement new的方式;為了簡化起見,我直接呼叫解構函式來銷燬元素,而在考慮效率的情況下一般會先判斷元素是否為non-trivial型別。

關於 trivial 和 non-trivial 的含義,參見:stack overflow

2.空間配置器

cghAlloc.h是空間配置器檔案,空間配置器負責記憶體的申請和回收。

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:cghAllocator空間配置器的實現程式碼
******************************************************************/

#ifndef _CGH_ALLOC_
#define _CGH_ALLOC_

#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>


namespace CGH
{
	#pragma region 記憶體分配和釋放函式、元素的構造和解構函式
	// 記憶體分配
	template<class T>
	inline T* _allocate(ptrdiff_t size, T*)
	{
		set_new_handler(0);
		T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
		if (tmp == 0)
		{
			std::cerr << "out of memory" << std::endl;
			exit(1);
		}
		return tmp;
	}

	// 記憶體釋放
	template<class T>
	inline void _deallocate(T* buffer)
	{
		::operator delete(buffer);
	}

	// 元素構造
	template<class T1, class  T2>
	inline void _construct(T1* p, const T2& value)
	{
		new(p)T1(value);
	}

	// 元素析構
	template<class T>
	inline void _destroy(T* ptr)
	{
		ptr->~T();
	}
	#pragma endregion

	#pragma region cghAllocator空間配置器的實現
	template<class T>
	class cghAllocator
	{
	public:
		typedef T			value_type;
		typedef T*			pointer;
		typedef const T*	const_pointer;
		typedef T&			reference;
		typedef const T&	const_reference;
		typedef size_t		size_type;
		typedef ptrdiff_t	difference_type;

		template<class U>
		struct rebind
		{
			typedef cghAllocator<U> other;
		};

		static pointer allocate(size_type n, const void* hint = 0)
		{
			return _allocate((difference_type)n, (pointer)0);
		}

		static void deallocate(pointer p, size_type n)
		{
			_deallocate(p);
		}

		static void deallocate(void* p)
		{
			_deallocate(p);
		}

		void construct(pointer p, const T& value)
		{
			_construct(p, value);
		}

		void destroy(pointer p)
		{
			_destroy(p);
		}

		pointer address(reference x)
		{
			return (pointer)&x;
		}

		const_pointer const_address(const_reference x)
		{
			return (const_pointer)&x;
		}

		size_type max_size() const
		{
			return size_type(UINT_MAX / sizeof(T));
		}
	};
	#pragma endregion

	#pragma region 封裝STL標準的空間配置器介面
	template<class T, class Alloc = cghAllocator<T>>
	class simple_alloc
	{
	public:
		static T* allocate(size_t n)
		{
			return 0 == n ? 0 : (T*)Alloc::allocate(n*sizeof(T));
		}

		static T* allocate(void)
		{
			return (T*)Alloc::allocate(sizeof(T));
		}

		static void deallocate(T* p, size_t n)
		{
			if (0 != n)Alloc::deallocate(p, n*sizeof(T));
		}

		static void deallocate(void* p)
		{
			Alloc::deallocate(p);
		}
	};
	#pragma endregion
}

#endif


classcghAllocator是空間配置器類的定義,主要的四個函式的意義如下:allocate函式分配記憶體,deallocate函式釋放記憶體,construct構造元素,destroy析構元素。這四個函式最終都是通過呼叫_allocate、_deallocate、_construct、_destroy這四個行內函數實現功能。

我們自己寫的空間配置器必須封裝一層STL的標準介面,

template<classT, class Alloc = cghAllocator<T>>
class simple_alloc


         構造與解構函式、空間配置器是最最基本,最最底層的部件,把底層搭建好之後我們就可以著手設計deque了。

3.deque的迭代器

         我們首先設計deque的迭代器,我把迭代器的內部結構分解為一下幾個部分

1.      一堆typedef、成員變數的宣告。最重要的成員變數是node,它是銜接管控中心和迭代器的樞紐;

2.      迭代器管控中心和cghDeque管控中心的銜接,只有一個函式:set_node,初始化迭代器的成員變數,更重要的是與deque的管控中心連線;

3.      確定每個緩衝區的大小:只有一個函式buffer_size();

4.      迭代器基本操作:deque的迭代器要保證random access;


迭代器程式碼的註釋已經寫得十分詳細了,有疑問的地方我都給出了說明,童鞋們可以參考迭代器的內部結構來總體把握迭代器的框架,通過註釋來理解迭代器的工作原理。

cghDequeIterator.h(迭代器)程式碼如下:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:cghDeque的迭代器的實現程式碼
******************************************************************/

#ifndef _CGH_DEQUE_ITERATOR_
#define _CGH_DEQUE_ITERATOR_

#include <memory>
namespace CGH{

	template<class T, class Ref, class Ptr, size_t BufSiz>
	struct __deque_iterator{

		#pragma region typedef和成員變數的定義

		typedef T			value_type;
		typedef Ptr			pointer;
		typedef Ref			reference;
		typedef size_t		size_type;
		typedef ptrdiff_t	difference_type; // ptrdiff_t的使用要#include <memory>
		typedef T**			map_pointer; // 迭代器所屬緩衝區,該緩衝區由cghDeque的管控中心管理
		typedef __deque_iterator self;
		typedef __deque_iterator<T, T&, T*, BufSiz>		iterator;

		T* cur; // 當前位置
		T* first; // 緩衝區頭部
		T* last; // 緩衝區尾部
		map_pointer	node; // 迭代器所屬緩衝區,該緩衝區由cghDeque的管控中心管理

		#pragma endregion

		#pragma region 迭代器管控中心和cghDeque管控中心的銜接

		/*
			輸入引數:new_node,cghDeque傳過來的緩衝區位置(管控中心的某個節點)
		*/
		void set_node(map_pointer new_node)
		{
			node = new_node; // 連線cghDeque管控中心的某個節點和迭代器緩衝區
			first = *new_node; // 緩衝區頭部
			last = first + difference_type(buffer_size()); // 緩衝區尾部
		}

		#pragma endregion

		#pragma region 確定每個緩衝區的大小

		/*
			緩衝區預設大小為512位元組
			1.如果BufSiz不為0,傳回BufSiz,表示buffer_size由使用者自定義
			2.如果BufSiz,表示buffer_size使用預設值,那麼如果元素大小(sizeof(T))小於512位元組,傳回size_t(512 / sizeof(T)
			  如果元素大小(sizeof(T))大於512位元組,傳回1
		*/

		size_t buffer_size()
		{
			return BufSiz != 0 ? BufSiz : (sizeof(T) < 512 ? size_t(512 / sizeof(T)) : size_t(1));
		}
		#pragma endregion
		
		#pragma region 迭代器基本操作

			#pragma region 解除引用

		reference operator*() const{ return *cur; }
		pointer operator->()const{ return &(operator*()); }

		#pragma endregion

			#pragma region 迭代器的單步移動

		self& operator++()
		{
			++cur; // 切換至下一個元素
			// 如果到所在緩衝區的尾端,注意緩衝區是前閉後開的空間,last是緩衝區結束的哨兵,到達last就該切換緩衝區了
			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; // 下一個緩衝區的最後一個元素
			}
			// 注意緩衝區是前閉後開的空間,last是緩衝區結束的哨兵,本身沒有意義,last的前一個元素才有正確的值域
			--cur;
			return *this;
		}

		self& operator--(int)
		{
			self tmp = *this;
			--*this;
			return tmp;
		}

		#pragma endregion

			#pragma region 迭代器的隨機移動

		/*
			實現隨機存取(random access)
		*/
		self& operator+=(difference_type n)
		{
			difference_type offset = n + (cur - first); // 偏移
			// 1.offset >= 0:向後偏移
			// 2.offset < difference_type(buffer_size()):偏移小於緩衝區長度
			if (offset >= 0 && offset < difference_type(buffer_size()))
			{
				cur += n;
			}
			else
			{
				difference_type node_offset = offset > 0
					? offset / difference_type(buffer_size()) // 向後偏移:確定管控中心的偏移的節點(偏移多少個緩衝區)
					: -difference_type((-offset - 1) / buffer_size()) - 1; // 向前偏移:確定管控中心的偏移的節點(偏移多少個緩衝區)
				set_node(node + node_offset); // 從管控中心中選擇新的節點,切換緩衝區
				cur = first + (offset - node_offset*difference_type(buffer_size()));
			}
			return *this;
		}

		/*
			實現隨機存取(random access)
		*/
		self operator+(difference_type n) const
		{
			self tmp = *this;
			return tmp += n;
		}

		self& operator-=(difference_type n)const{ return *this += -n; }

		self operator-(difference_type n)const
		{
			self tmp = *this;
			return tnp -= n;
		}

		difference_type operator-(const self& x)
		{
			return difference_type(buffer_size())*(node - x.node - 1) + (cur - first) + (x.last - x.cur);
		}

		reference operator[](difference_type n)const{ return *(*this + n); }

		#pragma endregion

			#pragma region 迭代器的相互比較

		bool operator==(const self& x)const{ return cur == x.cur; }

		bool operator!=(const self& x)const{ return cur != x.cur; }

		bool operator<(const self& x)const
		{
			return (node == x.node) ? (cur < x.cur) : (node < x.node);
		}

		#pragma endregion

		#pragma endregion
	};
}

#endif

4.deque

重頭戲來啦:deque的實現。

Deque的內部結構我用region分為了以下幾類:

1.      一堆typedef、成員變數的定義,最重要的成員變數是map(管控中心),是deque實現頭尾插刪,動態擴容的靈魂;

2.      輔助函式:buffer_size(),確定每個緩衝區的大小;

3.      初始化:deque的建構函式,以及建構函式引出的一大堆東西~~

4.      查詢操作:返回deque的大小、頭部、尾部等等;

5.      新增和刪除:前插、後插、清空

 

deque程式碼的註釋已經寫得十分詳細了,有疑問的地方我都給出了說明,童鞋們可以參考deque的內部結構來總體把握deque的框架,通過註釋來理解deque的工作原理。

cghDeque.h:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:cghDeque的實現程式碼
******************************************************************/

#ifndef _CGH_DEQUE_
#define _CGH_DEQUE_

#include "cghAlloc.h"
#include "globalConstruct.h"
#include "cghDequeIterator.h"
#include <algorithm>

namespace CGH{
	template<class T, class Alloc = cghAllocator<T>, size_t BufSiz = 0>
	class cghDeque{

		#pragma region typedef和成員變數的定義
	public:
		typedef T				value_type;
		typedef value_type*		pointer;
		typedef value_type&		reference;
		typedef size_t			size_type;
		typedef ptrdiff_t	difference_type;
		typedef typename __deque_iterator<T, T&, T*, BufSiz>::iterator iterator; // 迭代器,#include "cghDequeIterator.h"

	protected:
		typedef pointer* map_pointer;
	protected:
		iterator start;
		iterator finish;
		map_pointer map; // 管控中心
		size_type map_size;

		#pragma endregion

		#pragma region 輔助函式

	protected:
		/*
			管控中心(map)每個節點指向的緩衝區大小
			緩衝區預設大小為512位元組
			1.如果BufSiz不為0,傳回BufSiz,表示buffer_size由使用者自定義
			2.如果BufSiz,表示buffer_size使用預設值,那麼如果元素大小(sizeof(T))小於512位元組,傳回size_t(512 / sizeof(T)
			如果元素大小(sizeof(T))大於512位元組,傳回1
		*/
		size_t buffer_size()
		{
			return BufSiz != 0 ? BufSiz : (sizeof(T) < 512 ? size_t(512 / sizeof(T)) : size_t(1));
		}

		#pragma endregion
				
		#pragma region cghDeque的初始化

	protected:
		typedef simple_alloc<value_type, Alloc>		data_allocator; // cghDeque節點的空間配置器
		typedef simple_alloc<pointer, Alloc>		map_allocator; // cghDeque管控中心(map)的空間配置器
	public:
		/*
			建構函式
		*/
		cghDeque(int n, const value_type& value) :start(), finish(), map(0), map_size(0)
		{
			fill_initialize(n, value);
		}
		/*
			預設建構函式
		*/
		cghDeque() :start(), finish(), map(0), map_size(0) { fill_initialize(0, 0); }
	protected:

		/*
		各種初始化
		*/
		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)
			{
				std::uninitialized_fill(*cur, *cur + buffer_size(), value);
			}
			std::uninitialized_fill(finish.first, finish.cur, value);
		}

		/*
		初始化管控中心(map)、管控中心每個節點管理的緩衝區大小
		*/
		void create_map_and_nodes(size_type num_elements)
		{
			// 需要初始化的節點數 = (元素個數 / 每個緩衝區可容納的元素個數) + 1
			size_type num_nodes = num_elements / buffer_size() + 1;
			// 一個管控中心(map)要管理幾個節點,最少8個,最多是“所需節點數 + 2”
			map_size = std::max(initial_map_size(), num_nodes + 2); // std::max 需要 #include <algorithm>
			// 配置出一個具有“map_size個節點”的管控中心(map)
			map = map_allocator::allocate(map_size);
			// 令nstart和nfinish指向管控中心(map)所有節點的中間
			// 保持在中間,可使頭尾兩端擴充能量一樣大
			map_pointer nstart = map + (map_size - num_nodes) / 2;
			map_pointer nfinish = nstart + num_nodes - 1;

			// 為管控中心(map)的每個現用節點配置緩衝區,所有緩衝區加起來就是cghDeque的可用空間
			map_pointer cur;
			for (cur = nstart; cur <= nfinish; ++cur)
			{
				*cur = allocate_node(); // 配置緩衝區
			}
			start.set_node(nstart); // 銜接start迭代器
			finish.set_node(nfinish); // 銜接finish迭代器
			start.cur = start.first; // 確定start迭代器的遊標
			finish.cur = finish.first + num_elements % buffer_size(); // 確定finish迭代器的遊標
		}

		/*
		管控中心(map)最小節點數
		*/
		size_type initial_map_size(){ return (size_type)8; }

		/*
		配置管控中心(map)每個節點的緩衝區大小
		*/
		pointer allocate_node()
		{
			return data_allocator::allocate(buffer_size() / sizeof(T));
		}

		/*
		釋放管控中心(map)節點的緩衝區
		*/
		void deallocate_node(void* node)
		{
			data_allocator::deallocate(node);
		}

		#pragma endregion

		#pragma region cghDeque的查詢操作

	public:
		iterator begin(){ return start; } // 獲得cghDeque的頭部

		iterator end(){ return finish; } // 獲得cghDeque的尾部

		reference operator[](size_type n){ return start[difference_type(n)]; } // 獲得cghDeque第n個元素

		reference front(){ return *start; } // 獲得cghDeque的頭部的值

		/*
			因為緩衝區是前閉後開的區間,獲得cghDeque的尾部時需要finish回退一個步長
		*/
		reference back()
		{
			iterator tmp = finish;
			--tmp;
			return *tmp;
		}

		/*
			獲得cghDeque的長度
		*/
		size_type size()
		{
			return finish - start;
		}

		size_type max_size() const{ return finish - start; } // 獲得cghDeque的最大程長度

		bool empty()const{ return finish == start; } // cghDeque是否為空

		#pragma endregion

		#pragma region cghDeque元素的新增和刪除操作

	public:

		/*
			在cghDeque尾部插入元素
		*/
		void push_back(const value_type& t)
		{
			// 緩衝區是前閉後開的區間,finish迭代器的last元素做哨兵
			// 如果到達finish.last,說明緩衝區尾部已滿,呼叫push_back_aux,來到管控中心(map)的下一個節點,也就是下一個緩衝區
			if (finish.cur != finish.last - 1)
			{
				construct(finish.cur, t);
				++finish.cur;
			}
			else
			{
				push_back_aux(t);
			}
		}
		
		/*
			在cghDeque頭部插入元素
		*/
		void push_front(const value_type& t)
		{
			// 如果沒有到達緩衝區頭部,說明緩衝區前半部分有剩餘,直接插入
			// 如果到達start.first,說明緩衝區頭部已滿,呼叫push_front_aux,來到管控中心(map)的上一個節點,也就是上一個緩衝區
			if (start.cur != start.first)
			{
				construct(start.cur - 1, t);
				--start.cur;
			}
			else
			{
				push_front_aux(t);
			}
		}

		/*
			從cghDeque尾部彈出元素
		*/
		void pop_back()
		{
			// 如果沒有到達finish迭代器的頭部,直接destroy
			if (finish.cur != finish.first)
			{
				--finish.cur;
				destroy(finish.cur);
			}
			else
			{
				pop_back_aux(); // 如果到達finish的頭部,說明我們要銷燬的元素跨了緩衝區,我們要到上一個緩衝區去刪除元素
			}
		}

		/*
			從cghDeque頭部彈出元素
		*/
		void pop_front()
		{
			// 如果沒有到達start迭代器的尾,直接destroy
			if (start.cur != start.last - 1)
			{
				destroy(start.cur);
				++start.cur;
			}
			else
			{
				pop_front_aux(); // 如果到達start的尾,說明我們要銷燬的元素跨了緩衝區,我們要到下一個緩衝區去刪除元素
			}
		}

		/*
			清除cghDeque的所有元素
		*/
		void clear()
		{
			// [start.node + 1, finish.node)是滿員的,所以先清除[start.node + 1, finish.node)這段緩衝區的元素
			for (map_pointer node = start.node + 1; node < finish.node; ++node)
			{
				destroy(*node, *node + buffer_size());
				data_allocator::deallocate(*node, buffer_size());
			}
			// 如果start.node != finish.node,說明start迭代器和finish迭代器跨了管控中心的節點,要分別清除
			if (start.node != finish.node)
			{
				destroy(start.cur, start.last);
				destroy(finish.first, finish.last);
				data_allocator::deallocate(finish.first, buffer_size());
			}
			else // 如果start.node == finish.node,說明start迭代器和finish迭代器在管控中心同一個節點中
			{
				destroy(start.cur, finish.cur);
			}
			finish = start; // 不要忘了!
		}
	protected:

		/*
			緩衝區溢位時的後插
		*/
		void push_back_aux(const value_type& t)
		{
			value_type t_copy = t;
			*(finish.node + 1) = allocate_node(); // 給管控中心(map)的下一個節點(也就是下一個緩衝區)分配記憶體
			construct(finish.cur, t_copy); // 構造元素
			finish.set_node(finish.node + 1); // 重置finish迭代器,指向下一個緩衝區
			finish.cur = finish.first; // 重置finish迭代器的遊標
		}

		/*
			緩衝區溢位時的前插
		*/
		void push_front_aux(const value_type& t)
		{
			value_type t_copy = t;
			*(start.node - 1) = allocate_node(); // 給管控中心(map)的上一個節點(也就是上一個緩衝區)分配記憶體
			start.set_node(start.node - 1); // 重置start迭代器,指向上一個緩衝區
			start.cur = start.last - 1; // 重置start迭代器的遊標
			construct(start.cur, t_copy); // 構造元素
		}

		/*
			緩衝區溢位時的後刪
		*/
		void pop_back_aux()
		{
			deallocate_node(finish.first); // 釋放記憶體
			finish.set_node(finish.node - 1); // 上一個緩衝區
			finish.cur = finish.last - 1; // 重置元素,注意緩衝區是前閉後開
			destroy(finish.cur); // 析構上一個緩衝區的最後一個元素
		}

		/*
			緩衝區溢位時的前刪
		*/
		void pop_front_aux()
		{
			destroy(start.cur); // 析構元素
			deallocate_node(start.first); // 釋放記憶體
			start.set_node(start.node + 1); // 下一個緩衝區
			start.cur = start.first; // 重置遊標
		}

		#pragma endregion
	};
}
#endif



 5.測試

最後是測試環節,測試的主要內容已在註釋中說明

test_cghDeque.cpp:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  檔名稱:cghDeque容器的測試程式碼
******************************************************************/

#include "stdafx.h"
#include "cghVector.h"
#include "cghDeque.h"
using namespace::std;

int _tmain(int argc, _TCHAR* argv[])
{
	using namespace::CGH;



	/***************************************************************************************/
	/***************************************************************************************/
	std::cout << "************************初始化、前插、後插測試************************" << endl;
	std::cout << endl;
	cghDeque<int> test(1, 1); // 初始化
	test.push_back(2); // 後插
	test.push_back(3); // 後插
	test.push_back(4); // 後插
	test.push_front(0); // 前插
	test.push_front(-1); // 前插

	std::cout << "當前元素:";
	for (cghDeque<int>::iterator iter = test.begin(); iter != test.end(); ++iter){
		std::cout << *iter << ",";
	}
	std::cout << endl;
	std::cout << endl;
	std::cout << "長度:" << test.size() << endl;
	std::cout << endl;




	/***************************************************************************************/
	/***************************************************************************************/
	std::cout << "************************前刪、後刪測試************************" << endl;
	std::cout << endl;
	test.pop_front(); // 前刪
	test.pop_back(); // 後刪
	std::cout << "當前元素:";
	for (cghDeque<int>::iterator iter = test.begin(); iter != test.end(); ++iter){
		std::cout << *iter << ",";
	}
	std::cout << endl;
	std::cout << endl;
	std::cout << "長度:" << test.size() << endl;
	std::cout << endl;




	/***************************************************************************************/
	/***************************************************************************************/
	std::cout << "************************清空測試************************" << endl;
	std::cout << endl;
	test.clear(); // 前刪
	std::cout << "當前元素:";
	for (cghDeque<int>::iterator iter = test.begin(); iter != test.end(); ++iter){
		std::cout << *iter << ",";
	}
	std::cout << endl;
	std::cout << endl;
	std::cout << "長度:" << test.size() << endl;
	std::cout << endl;




	/***************************************************************************************/
	/***************************************************************************************/
	std::cout << "************************跨主控節點後插測試************************" << endl;
	std::cout << endl;
	std::cout << "緩衝區預設大小為512位元組,一個int佔4位元組,512 / 4 = 128" << endl << endl;
	std::cout << "當插入的元素量 > 128 時就會跨主控節點" << endl;
	std::cout << endl;
	test.clear(); // 前刪
	for (int i = 0; i < 150; i++){
		test.push_back(i);
	}
	std::cout << "當前元素:";
	for (cghDeque<int>::iterator iter = test.begin(); iter != test.end(); ++iter){
		std::cout << *iter << ",";
	}
	std::cout << endl;
	std::cout << endl;
	std::cout << "長度:" << test.size() << endl;
	std::cout << endl;




	/***************************************************************************************/
	/***************************************************************************************/
	std::cout << "************************跨主控節點前插測試************************" << endl;
	std::cout << endl;
	std::cout << "緩衝區預設大小為512位元組,一個int佔4位元組,512 / 4 = 128" << endl << endl;
	std::cout << "當插入的元素量 > 128 時就會跨主控節點" << endl;
	std::cout << endl;
	test.clear(); // 前刪
	for (int i = 0; i < 150; i++){
		test.push_front(i);
	}
	std::cout << "當前元素:";
	for (cghDeque<int>::iterator iter = test.begin(); iter != test.end(); ++iter){
		std::cout << *iter << ",";
	}
	std::cout << endl;
	std::cout << endl;
	std::cout << "長度:" << test.size() << endl;
	std::cout << endl;

	std::cout << "************************測試結束************************" << endl;
	std::cout << endl;
	system("pause");
	return 0;
}


測試結果圖:


6.擴充套件討論

在STL簡單stack的實現(傳送門:點選開啟連結)中,我們為什麼選擇deque作為stack的底層結構,而不是vector?

我們從以下三個方面來分析:

1.      vector和deque的空間使用效率;

2.      vector和deque的迭代器訪問元素效率;

3.      stack的應用場景;


通過上表對比,我們可以得出如下結論:

1.             vector是真正意義上的連續空間,而deque底層不連續,只是使用者看來連續罷了,vector容量溢位時要做三步:1.分配新空間;2.資料移動;釋還舊空間。Vector在空間使用上的效率遠低於deque;

2.             vector底層使用連續空間,故可以採用普通指標作為迭代器,訪問元素的效率高;deque在底層不連續,迭代器設計較複雜,在隨機訪問元素時效率低。

Stack應用場景包括:函式呼叫時傳遞引數、儲存現場,區域性變數分配和使用。

注意,我們只能訪問棧頂元素,迭代器一次只移動一個步長,沒有隨機訪問元素的情況,這意味著採用普通指標和deque專屬迭代器來獲得棧頂元素的效率相差不大。

得出結論一:vector和deque的迭代器在訪問stack元素時打個平手。

但是,程式執行過程中stack會不斷的變大縮小,對空間的伸縮性要求很高。deque可以自由組合分段空間,而vector顯得有些死板,空間不夠就搬家。

得出結論二:deque在空間上使用更靈活,更符合stack的應用場景。

綜合結論一、二,我們可以得出最終結論:deque更適合作為stack的底層結構。


相關推薦

STL 簡單 deque實現

Vector是單向開口的連續線性空間,而Deque是雙向開口的連續線性空間,可以在頭尾兩端插入和刪除元素,而且deque沒有容量(capacity)的概念,因為deque動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。          Deque採用一

stldeque的一些簡單用法

#include <iostream> #include <cstring> #include <cstdio> #include <deque> using namespace std; int main() {     in

STL 簡單 set 和 multiset 的實現

         set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。          multi

STL 原始碼剖析 deque實現原始碼

#ifndef _HJSTL_DEQUE_H_ #define _HJSTL_DEQUE_H_ /* * Author:hujian * Time:2016/4/28 * discription:this file is about deque structur

STL 簡單 iterator 的實現(含原始碼)

        我使用vs2015寫的程式(原始碼下載) STL的中心思想在於將容器(container)和演算法(algorithms)分開,彼此獨立設計,最後再以一貼膠著劑將它們撮合在一起,而這個膠著劑就是迭代器(iterator)。          迭代器是訪問容器

STL 簡單 copy 演算法的實現

1.簡介          不論是對客戶端或對STL內部而言,copy() 都是一個常常被呼叫的函式。由於copy進行的是複製操作,而複製操作不外乎運用賦值運算子(assignment operator)或複製建構函式(copy constructor),但是某些元素的型別

STL 簡單紅黑樹的實現

1.紅黑樹簡介 二叉搜尋樹能夠提供對數的元素插入和訪問。二叉搜尋樹的規則是:任何節點的鍵值一定大於其左子樹的每一個節點值,並小於右子樹的每一個節點值。 常見的二叉搜尋樹有AVL-tree、RB-tree(紅黑樹)。紅黑樹具有極佳的增、刪、查效能,故我們選擇紅黑樹作為關聯式容

STL 簡單 list 的實現

相較於vector的連續線性空間,list就顯得複雜許多,它的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。因此,list對於空間的運用有絕對的精準,一點不浪費。Vector的插入操作可能造成記憶體的重新配置,但是List不會。          List不再能

使用C++STL中的deque實現作業系統FIFO、LRU頁面置換演算法

#include <iostream> #include <deque>//雙端佇列所在的標頭檔案 #include <algorithm>//find()函式所在的標頭檔案 using namespace std; cons

RabbitMQ第二篇:java簡單實現RabbitMQ

ech pre cer wait clas comm amqp cti 一次 前言:在這裏我將用java來簡單的實現rabbitMQ。下面我們帶著下面問題來一步步的了解和學習rabbitMQ。 1:如果消費者連接中斷,這期間我們應該怎麽辦 2:如何做到負載均衡 3:如何有效

簡單shell實現

.net 執行 buffer 參數 tdi efi command 分開 結束 http://blog.csdn.net/lishuhuakai/article/details/11928055 #include <stdio.h> #include &l

(原創)Maven+Spring+CXF+Tomcat7 簡單例子實現webservice

produces per back targe xsd lean listener ans 控制 這個例子需要建三個Maven項目,其中一個為父項目,另外兩個為子項目 首先,建立父項目testParent,選擇quickstart: 輸入項目名稱和模塊名稱,然後創建:

做視頻直播目前最簡單實現方式是什麽

國內 快速 在線 需要 href 視頻播放 blank www spa 網友問題: 在遠程教人寫東西 ,原來用雲直播服務平臺,但國內的平臺都需要實名。目前有什麽快速的搭建 直播服務器server ,能接受 OBS 軟件來的流,並且可以通過網頁看出來就好。其實就是給老家的一個

(C++ STL)list的實現

throw 實現 locate 當前 模板類 spl 會有 splice 重定義 #include <iostream> using namespace std; //採用叠代器和空間配置器所實現的雙向鏈表的基本功能 template<

簡單ANN實現二次曲線擬合

plt float evel step num one 輸出 numpy des 代碼: 1 import os 2 os.environ[‘TF_CPP_MIN_LOG_LEVEL‘]=‘2‘ 3 import tensorflow as tf 4 i

簡單js實現豎行tab切換

block webkit des .class utf eight ref class clas <!DOCTYPE ><html lang="ru"><head><title>簡單js實現豎行tab切換</title&

爬蟲基礎知識與簡單爬蟲實現

春秋 屬性 str 版本 page 2017年 light install defaults css規則:選擇器,以及一條或者多條生命。 selector{declaration1;,,,;desclarationN} 每條聲明是由一個屬性和一個值組成 propert

C 雙向鏈表的簡單排序實現

rtb swap 結構 code str 表頭 urn else 重新 今天偶爾看到了C結構體的單項鏈表。 於是重新溫習了下雙向鏈表,重寫了下雙向鏈表的簡單排序實現,當做溫習總結吧。 先定義雙向鏈表 1 struct Student{ 2 int studentI

SOCKET簡單爬蟲實現代碼和使用方法

apple 頭信息 cti 實例 組元 目錄 agent uniq nec 抓取一個網頁內容非常容易,常見的方式有curl、file_get_contents、socket以及文件操作函數file、fopen等。 下面使用SOCKET下的fsockopen()函數訪問W

Flume自定義Source、Sink和Interceptor(簡單功能實現)

next generated lose 指定 char atomic -i 根據 進行   1.Event    event是flume傳輸的最小對象,從source獲取數據後會先封裝成event,然後將event發送到channel,sink從channel拿event消