STL 简单 deque 的实现

本文介绍了如何实现STL中的deque容器,包括构造函数、空间配置器、迭代器的详细设计,以及deque的内部结构和工作原理。通过对比vector和deque在空间使用效率和迭代器访问效率,阐述deque为何更适合作为stack的底层结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         我用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的底层结构。


### C++ STL `deque` 使用方法及特性 #### 一、基本概念 `std::deque<T>` 是标准模板库(STL)提供的一种双端队列容器,支持快速的随机访问以及高效的两端插入和删除操作。这种灵活性使得它适用于多种场景下的数据存储需求。 #### 二、主要功能与使用方式 ##### (一)创建与初始化 可以像其他STL容器一样通过默认构造函数来实例化一个空的`deque`对象;也可以指定初始大小或填充特定数量相同类型的元素进行初始化[^4]。 ```cpp // 创建一个整型dequeue,默认无参数则为空 std::deque<int> myDeque; // 初始化含有五个零值整数的dequeue std::deque<int> anotherDeque(5, 0); ``` ##### (二)元素存取 提供了`front()`获取头部元素引用,`back()`获得尾部元素引用的方法。对于任意位置上的元素,则可以直接利用下标运算符[]来进行读写操作。 ```cpp myDeque.push_back(1); // 向后追加新项 anotherDeque.front(); // 访问第一个元素 anotherDeque.back(); // 获取最后一个元素 anotherDeque[2]; // 索引第三个元素 (假设存在) ``` ##### (三)增删改查 - 插入:除了常规的`push_back()/push_front()`外,在C++20版本之后还增加了更灵活的方式——`emplace_*`系列成员函数允许直接在目标地点构建对象而无需先创建临时变量再转移所有权[^3]; - 删除:不仅能够分别针对前后两端调用`pop_back()/pop_front()`移除单个节点,还可以借助于新增API如`erase_if()`批量清除符合条件的所有条目[^1]; - 修改:可通过迭代器配合算法库中的工具完成复杂变换任务,比如下面的例子展示了如何将每一个数值翻倍处理[^2]: ```cpp std::for_each(myDeque.begin(), myDeque.end(), [](int& elem){ elem *= 2; }); ``` ##### (四)查询统计 为了方便开发者判断当前状态,内置了一些辅助性的属性接口,例如检测是否为空(`empty()`)或是计算总长度(`size()`)等. #### 三、内部结构特点分析 不同于连续内存布局的传统数组向量(vector),`deque`采用了分段式的管理策略,即将整个序列分割成若干固定大小的小块(block),每一块之间保持一定间隔以便后续扩展时能更容易找到合适的位置安插新的片段而不至于频繁触发大规模重定位动作。这样的设计既保留了一定程度上类似于链表那样动态调整的优势,又兼顾到了接近线性寻址所带来的性能提升效果. #### 四、适用范围对比总结 当面对需要频繁执行双向末端更新且偶尔涉及中间部分修改的应用场合时,选用`deque`往往可以获得更好的综合表现。然而值得注意的是由于其特殊的组织形式可能会造成缓存命中率下降进而影响到某些情况下顺序扫描效率,所以在实际项目选型过程中应当权衡利弊做出合理抉择.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值