STL 簡單 deque 的實現
Vector是單向開口的連續線性空間,而Deque是雙向開口的連續線性空間,可以在頭尾兩端插入和刪除元素,而且deque沒有容量(capacity)的概念,因為deque動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。
Deque採用一塊map作為主控,這裡所謂map是一小塊連續空間,其中每個元素(此處稱為節點,node)都是指標,指向另一段連續線性空間,稱之為緩衝區。
deque的實現需要以下幾個檔案:
1. globalConstruct.h,構造和解構函式檔案,位於//cghAllocator/
2. cghAlloc.h,空間配置器檔案,位於/
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採用一
stl之deque的一些簡單用法
#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消