5.STL简单链表(_cghList)的实现

Posted chengonghao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5.STL简单链表(_cghList)的实现相关的知识,希望对你有一定的参考价值。

        我用VS2013写的程序(github ),list版本的代码位于cghSTL/version/cghSTL-0.3.2.rar

相较于vector的连续线性空间,list就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间。因此,list对于空间的运用有绝对的精准,一点不浪费。Vector的插入操作可能造成内存的重新配置,但是List不会。

         List不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在存储空间在连续存在。由于list是一个双向链表,迭代器必须具有前移、后移的能力,所以list提供的是Bidirectional iterators。

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

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

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

3.      _cghList.h,list节点的实现,位于cghSTL/sequence containers/cghList/

4.      ListIterator.h,迭代器的实现,位于cghSTL/sequence containers/cghList/

5.      ListNode.h,list双向链表的实现,位于cghSTL/sequence containers/cghList/

6.      test_cghList.cpp,测试文件,位于cghSTL/test/

 

先看第一个,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

 

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<class T, class Alloc = cghAllocator<T>>
	class simple_alloc

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

         先看第一个,list的节点实现代码:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  功能:_cghList中元素的实现代码
******************************************************************/

#ifndef _CGH_LIST_NODE_
#define _CGH_LIST_NODE_

namespace CGH{
	// 定义双向链表节点类型
	template<typename T>
	struct __list_node
	{
		typedef void* void_pointer;
		void_pointer prev; // 指向前一个节点
		void_pointer next; // 指向后一个节点
		T data; // 节点的数据域
	};
}

#endif



         我们设计的是双向list,每个节点包含了两个指针,分别指向前一个节点和后一个节点,同时data代表节点的数据域。

        

         接下来设计list的迭代器:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  功能:_cghList的迭代器的实现代码
******************************************************************/

#ifndef _CGH_LIST_ITERATOR_
#define _CGH_LIST_ITERATOR_

#include "ListNode.h"
#include <memory>

namespace CGH{
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef __list_iterator<T, T&, T*>		iterator;
		typedef __list_iterator<T, Ref, Ptr>	self;

		typedef T					value_type;
		typedef Ptr					pointer;
		typedef Ref					reference;
		typedef __list_node<T>*		link_type;
		typedef size_t				size_type;
		typedef ptrdiff_t			difference_type;

		link_type node; // 联系迭代器和链表节点的纽带

		#pragma region 构造函数

		__list_iterator(link_type x) : node(x){}
		__list_iterator(){}
		__list_iterator(const iterator& x) :node(x.node){}

		#pragma endregion

		#pragma region 迭代器的基本操作

		bool operator==(const self& x) const { return node == x.node; }
		bool operator!=(const self& x) const { return node != x.node; }
		reference operator*() const { return (*node).data; }
		reference operator->() const { return &(operator*()); }

		// 迭代器前进一个节点
		self& operator++()
		{
			node = (link_type)((*node).next);
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			++*this;
			return tmp;
		}

		// 迭代器递减1,后退一个节点
		self& operator--()
		{
			node = (link_type)((*node).prev);
			return *this;
		}
		self operator--(int)
		{
			self tmp = *this;
			--*this;
			return tmp;
		}

		#pragma endregion
	};
}
#endif




         根据功能,我把迭代器划分成了两个region,第一个region是迭代器的构造函数,第二个region是迭代器的基础操作,比如前进后退,判断是否相等…。遗憾的是CSDN的代码编辑器不给力,网页上不能根据region折叠代码,有些童鞋看到大量代码会产生畏惧感,我们平时写代码要注意层次,划分region,功能分明,这样读起来会好很多。

         现在我们有了list节点和list迭代器,接着把节点和迭代器组合在一起,构成list:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  功能:_cghList的实现代码
******************************************************************/

#ifndef _CGH__LIST_
#define _CGH__LIST_

#include "ListNode.h"
#include "cghAlloc.h"
#include "globalConstruct.h"
#include "ListIterator.h"

namespace CGH{
	template<class T, class Alloc = cghAllocator<T>>
	class _cghList{
	protected:
		typedef __list_node<T>				list_node;
	public:
		typedef list_node*				link_type;
		typedef size_t					size_type;
		typedef typename __list_iterator<T, T&, T*>::iterator	iterator;

	#pragma region 内存控制、节点构造与析构
	protected:
		link_type node; // _cghList中唯一的节点指针,指向_cghList的尾部
		typedef simple_alloc<list_node, Alloc> list_node_allocator; // 定义空间配置器
		link_type get_node(){ return list_node_allocator::allocate(); } // 申请一个节点的内存空间
		void put_node(link_type p){ list_node_allocator::deallocate(p); } // 释放一个节点的内存空间

		/*
			创建一个节点:
			1.调用get_node申请节点内存;
			2.调用construct构造节点
		*/ 
		link_type create_node(const T& x)
		{
			link_type p = get_node();
			construct(&p->data, x);
			return p;
		}

		/*
			销毁一个节点:
			1.调用destroy析构节点;
			2.调用put_node释放内存
		*/
		void destroy_node(link_type p)
		{
			destroy(&p->data);
			put_node(p);
		}

	public:
		/*
			构造函数:
			调用empty_initialize
		*/
		_cghList(){ empty_initialize(); }
	protected:
		/*
			构造一个空链表:
			1.申请一个节点内存
			2.初始化尾节点
		*/
		void empty_initialize()
		{
			node = get_node();
			node->next = node;
			node->prev = node;
		}
	#pragma endregion 

	public:
		#pragma region 链表的查询操作

		/*
			返回头节点:
			_cghList中的node成员变量保存了链表的尾指针
			链表的尾指针不参与运算,仅标识链表结尾
		*/
		iterator begin()const { return (link_type)((*node).next); }

		/*
			返回尾节点:
			_cghList中的node成员变量保存了链表的尾指针
			链表的尾指针不参与运算,仅标识链表结尾
		*/
		iterator end()const{ return node; }

		/*
			判断链表是否为空:
			_cghList中的node成员变量保存了链表的尾指针
			链表的尾指针不参与运算,仅标识链表结尾
		*/
		bool empty() const{ return node->next == node; }

		/*
			返回链表长度:
			_cghList中的node成员变量保存了链表的尾指针
			链表的尾指针不参与运算,仅标识链表结尾
		*/
		size_type size()	const
		{
			size_type result = 0;
			iterator first = begin();
			iterator last = end();
			while (first != last)
			{
				++first;
				++result;
			}
			return result;
		}

		#pragma endregion

		#pragma region 插入链表元素,包括头插和尾插

		/*
			插入到链表头部
		*/
		void push_back(const T& x){ insert(end(), x); }

		/*
			插入到链表尾部
		*/
		void push_front(const T& x){ insert(begin(), x); }

		/*
			执行具体的插入操作
		*/
		iterator insert(iterator position, const T& x)
		{
			link_type tmp = create_node(x);
			tmp->next = position.node;
			tmp->prev = position.node->prev;
			(link_type(position.node->prev))->next = tmp;
			position.node->prev = tmp;
			return tmp;
		}

		#pragma endregion

		#pragma region 删除链表元素

		/*
			删除指定位置的链表节点
		*/
		iterator erase(iterator position)
		{
			link_type next_node = link_type(position.node->next);
			link_type prev_node = link_type(position.node->prev);
			prev_node->next = next_node;
			next_node->prev = prev_node;
			destroy_node(position.node);
			return iterator(next_node);
		}

		/*
			删除链表头节点
		*/
		void pop_front(){ erase(begin()); }

		/*
			删除链表尾节点
		*/
		void pop_back()
		{
			iterator tmp = end();
			erase(--tmp);
		}

		/*
			清除整个链表
		*/
		void clear()
		{
			link_type cur = (link_type)node->next; //拿到头结点
			while (cur != node)
			{
				iterator tmp = cur;
				cur = (link_type)cur->next;
				destroy_node(tmp.node);
			}
			node->next = node;
			node->prev = node;
		}

		/*
			移除节点值为 value 的链表节点
		*/
		void remove(const T& value)
		{
			iterator first = begin();
			iterator last = end();
			while (first != last)
			{
				iterator next = first;
				++next;
				if (*first == value)erase(first);
				first = next;
			}
		}

		/*
			清除链表中连续存放的有相同节点值的元素
		*/
		void unique(const T& value)
		{
			iterator first = begin();
			iterator last = end();
			if (first == last)return;
			iterator next = first;
			while (++next != last)
			{
				if (*first == *next)
				{
					erase(next);
				}
				else
				{
					first = next;
				}
				next = first;
			}
		}

		#pragma endregion
	};
}

#endif



 

         按照功能,我把list分为了4个region:

1.      内存控制、节点构造与析构;

2.      链表的查询操作;

3.      插入链表元素,包括头插和尾插;

4.      删除链表元素;

注释写的很详细,只是CSDN的代码编辑器太渣,网页上不能根据region折叠代码,有些童鞋看到大量代码会产生畏惧感,list的结构我截图如下:

 

最后我们测试一下_cghList,测试代码如下:

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

#include "stdafx.h"
#include "cghAlloc.h"
#include "globalConstruct.h"
#include "_cghList.h"
using namespace::std;

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

	_cghList<int> list;
	int i = 0;
	list.push_back(1); // 在list尾部插入1
	list.push_back(2); // 在list尾部插入2
	list.push_back(2); // 在list尾部插入2
	list.push_back(2); // 在list尾部插入2
	list.push_front(3); // 在list头部插入1

	std::cout << "-----------------插入元素----------------" << endl;
	std::cout << "在list尾部依次插入1、2、2、2,在list头部插入3" << endl;
	// 取得list的长度
	std::cout << "list的长度:" << list.size() << endl;
	// 遍历list
	for (_cghList<int>::iterator iter = list.begin(); iter != list.end(); iter++)
	{
		std::cout << "第" << i << "个元素:" << *iter << endl;
		i++;
	}

	std::cout << endl << endl << "-----------------值保留一个值等于2的元素----------------" << endl;
	// 值保留一个值等于2的元素
	list.unique(2);
	// 取得list的长度
	std::cout << "list的长度:" << list.size() << endl;
	// 遍历list
	i = 0;
	for (_cghList<int>::iterator iter = list.begin(); iter != list.end(); iter++)
	{
		std::cout << "第" << i << "个元素:" << *iter << endl;
		i++;
	}

	std::cout << endl << endl << "-----------------删除值等于2的元素----------------" << endl;
	list.remove(2);// 删除值等于2的元素
	// 取得list的长度
	std::cout << "list的长度:" << list.size() << endl;
	// 遍历list
	i = 0;
	for (_cghList<int>::iterator iter = list.begin(); iter != list.end(); iter++)
	{
		std::cout << "第" << i << "个元素:" << *iter << endl;
		i++;
	}

	std::cout << endl << endl << "-----------------清空list----------------" << endl;
	list.clear(); // 清空
	// 取得list的长度
	std::cout << "list的长度:" << list.size() << endl;
	// 遍历list
	i = 0;
	for (_cghList<int>::iterator iter = list.begin(); iter != list.end(); iter++)
	{
		std::cout << "第" << i << "个元素:" << *iter << endl;
		i++;
	}

	system("pause");
	return 0;
}



以上是关于5.STL简单链表(_cghList)的实现的主要内容,如果未能解决你的问题,请参考以下文章

一个简单的单向链表实现,Python

一个简单的单向链表实现,Python

SeqList 简单的线性表实现

代码片--实现一个简单的模版方法设计模式(获取一段程序运行的时间)

一个简单的时间片轮转内核代码的分析(课程作业)

python实现链表