我用VS2013写的程序(github ),deque版本的代码位于cghSTL/version/cghSTL-0.3.3.rar
Vector是单向开口的连续线性空间,而Deque是双向开口的连续线性空间,可以在头尾两端插入和删除元素,而且deque没有容量(capacity)的概念,因为deque动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。
Deque采用一块map作为主控,这里所谓map是一小块连续空间,其中每个元素(此处称为节点,node)都是指针,指向另一段连续线性空间,称之为缓冲区。
deque的实现需要以下几个文件:
1. globalConstruct.h,构造和析构函数文件,位于cghSTL/allocator/cghAllocator/
2. cghAlloc.h,空间配置器文件,位于cghSTL/allocator/cghAllocator/
3. cghDequeIterator.h,迭代器的实现,位于cghSTL/sequence containers/cghDeque/
4. cghDeque.h,cghDeque的实现,位于cghSTL/sequence containers/cghDeque/
5. test_cghDeque.cpp,测试代码,位于cghSTL/test/
1.构造函数
先看第一个,globalConstruct.h构造函数文件
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:全局构造和析构的实现代码
******************************************************************/
#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.
*
* chengonghao@yeah.net
*
* 功能: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.
*
* chengonghao@yeah.net
*
* 功能: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.
*
* chengonghao@yeah.net
*
* 功能: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.
*
* chengonghao@yeah.net
*
* 文件名称: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的底层结构。