STL 简单 stack 的实现

本文详细介绍了如何使用VS2013实现自己的stack容器,包括构造与析构、空间配置器、stack的具体实现及测试过程。同时探讨了为何选择deque而非vector作为stack的底层实现。

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

         我用VS2013写的程序(传送门:github ),stack版本的代码位于cghSTL/version/cghSTL-0.3.4.rar

         stack是一种先进后出(first in last out)的数据结构。它只有一个出口,除了栈顶以外,没有任何其他方式存取元素。

stack没有遍历行为,故不需要迭代器,这让stack的设计变的非常简单,我们以deque作为缺省情况下的stack底层结构。看懂stack的实现,需要先知道deque是怎么做出来的,关于deque,请看另一篇博客,传送门:STL简单deque的实现

         由于stack以deque作为底层容器完成工作,这种具有“修改某接口,形成另一种风貌”的东西,我们称之为adapter(接配器),因此stack往往不被归类为容器,而被归为容器的接配器。

        

stack的实现需要以下几个文件:

1.      globalConstruct.h,构造和析构函数文件,位于cghSTL/allocator/cghAllocator/

2.      cghAlloc.h,空间配置器文件,位于cghSTL/allocator/cghAllocator/

3.      deque_stack.h,stack的实现,位于cghSTL/sequence containers/cghStack/

4.      test_deque_stack.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>>
classsimple_alloc


         构造与析构函数、空间配置器是最最基本,最最底层的部件,把底层搭建好之后我们就可以着手设计stack了。


3.stack的实现

Stack的底层用cghDeque实现,关于cghDeque,请看另一篇博客,传送门:STL简单deque的实现

deque_stack.h

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  文件名称:stack的实现代码
******************************************************************/

#ifndef _CGH_DEQUE_STACK_
#define _CGH_DEQUE_STACK_

#include "cghDeque.h"
#include"cghAlloc.h"
#include "globalConstruct.h"

namespace CGH{
	/*
		采用cghDeque作为stack的底层实现
	*/
	template<class T, class sequence = cghDeque<T>>
	class cghStack{
	public:
		typedef typename sequence::value_type		value_type;
		typedef typename sequence::size_type		size_type;
		typedef typename sequence::reference		reference;

	protected:
		sequence s;

	public:
		cghStack() :s(){ } // 构造函数
		bool empty() const{ return s.empty(); } // stack是否为空
		size_type size() { return s.size(); } // stack的长度
		reference top() { return s.back(); } // 栈顶元素
		void push(const value_type& x){ s.push_back(x); } // 压栈
		void pop(){ s.pop_back(); } // 弹栈
	};
}

#endif


4.测试

测试环节的主要内容已在注释中说明

test_deque_stack.cpp:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  文件名称:stack的测试代码
******************************************************************/

#include "stdafx.h"
#include "deque_stack.h"

using namespace::std;

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

	cghStack<int> test;
	std::cout << "************************压栈测试************************" << endl << endl;
	std::cout << "压入元素:";
	for (int i = 0; i < 4; ++i)
	{
		std::cout << i << ",";
		test.push(i);
	}
	std::cout << endl << endl << "栈顶元素:";
	std::cout << test.top() << endl << endl;
	std::cout << "栈的长度:" << test.size() << endl;
	std::cout << endl << endl;


	std::cout << "************************弹栈栈测试************************" << endl << endl;
	std::cout << "依次弹出:" << endl << endl;
	for (int i = 0; i < 4; ++i)
	{
		std::cout << "\t" << test.top() << endl << endl;
		test.pop();
	}
	std::cout << "栈的长度:" << test.size() << endl;
	std::cout << endl << endl;
	std::cout << "************************测试结束************************" << endl;

	system("pause");
	return 0;
}


效果图:


5.扩展讨论

为什么我们优先使用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的底层结构。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值