1. 程式人生 > >STL 簡單 list 的實現

STL 簡單 list 的實現

相較於vector的連續線性空間,list就顯得複雜許多,它的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。因此,list對於空間的運用有絕對的精準,一點不浪費。Vector的插入操作可能造成記憶體的重新配置,但是List不會。

         List不再能夠像vector一樣以普通指標作為迭代器,因為其節點不保證在儲存空間在連續存在。由於list是一個雙向連結串列,迭代器必須具有前移、後移的能力,所以list提供的是Bidirectional iterators。

list的實現需要以下幾個檔案:

1.      globalConstruct.h,構造和解構函式檔案,位於//cghAllocator

/

2.      cghAlloc.h,空間配置器檔案,位於//cghAllocator/

4.      ListIterator.h,迭代器的實現,位於//cghList/

5.      ListNode.h,list雙向連結串列的實現,位於//cghList/

6.      test_cghList.cpp,測試檔案,位於/test/

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

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

         構造與解構函式、空間配置器是最最基本,最最底層的部件,把底層搭建好之後我們就可以著手設計list了。

         先看第一個,list的節點實現程式碼:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:_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.
*
*  [email protected]
*
*  功能:_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.
*
*  [email protected]
*
*  功能:_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.
*
*  [email protected]
*
*  檔名稱:_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;
}



相關推薦

STL 簡單 list實現

相較於vector的連續線性空間,list就顯得複雜許多,它的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。因此,list對於空間的運用有絕對的精準,一點不浪費。Vector的插入操作可能造成記憶體的重新配置,但是List不會。          List不再能

C++ STLList實現

概述     List是STL中常用的容器之一,List將元素按順序儲存到連結串列中,在快速刪除和快速插入方面比vector高出許多。STL中的list是一雙向連結串列,具有指向前一節點和後一節點的指標。那麼我們開始寫一個屬於自己的List吧。 list的節點    

STLlist實現降序排列

       STL list可以對基本資料、字串直接呼叫sort()函式預設做升序排列,但是對於降序排列或者對非基本資料型別排序(物件、陣列等)需要藉助operator()函式實現,這點和Java中的List很相似。 具體呼叫函式: list.sort(Cmpare())

STLlist簡單剖析以及各種函式的實現

STL中的list是比較常用的容器,對於任何位置的元素插入或元素移除,list永遠是常數。 list中的迭代器在插入和刪除後,仍然有效,但是耦合操作splice操作可能使迭代器失效,而vector就不成立了。 list節點 template <

JAVA用於List實現簡單的學生管理系統

作為一名JAVA的程式設計師,無論初學者也好大神也好,學生管理系統是個很好例子,初學者用陣列、list等來寫簡單的學生管理系統,大神則是用swing+資料庫做有介面的學生管理系統,廢話不多說,今天我就用List來實現學生管理系統。 學生管理系統主要針對學生,我

List實現簡單購物車類

import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpSession; /** * 購物車 * 現實中是用籃子或車來裝商品的 * 好比記憶體中的List

stllist的sort演算法實現

STL中有一個std::sort演算法,但它是不支援std::list的,因為list不提供RandomIterator的支援,但list自己提供了sort演算法,把list的元素按從小到大的方式來排序,程式碼長度到不長,但真是難以讀懂,後來扣持了一下午終於搞明白了,貼個總

STL 簡單 set 和 multiset 的實現

         set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。          multi

STL 簡單 deque 的實現

Vector是單向開口的連續線性空間,而Deque是雙向開口的連續線性空間,可以在頭尾兩端插入和刪除元素,而且deque沒有容量(capacity)的概念,因為deque動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。          Deque採用一

STL 簡單 iterator 的實現(含原始碼)

        我使用vs2015寫的程式(原始碼下載) STL的中心思想在於將容器(container)和演算法(algorithms)分開,彼此獨立設計,最後再以一貼膠著劑將它們撮合在一起,而這個膠著劑就是迭代器(iterator)。          迭代器是訪問容器

STL 簡單 copy 演算法的實現

1.簡介          不論是對客戶端或對STL內部而言,copy() 都是一個常常被呼叫的函式。由於copy進行的是複製操作,而複製操作不外乎運用賦值運算子(assignment operator)或複製建構函式(copy constructor),但是某些元素的型別

用C++實現STL容器list

#ifndef LIST_H_ #define LIST_H_ //#include "Node.h" //#include "const_iterator.h" template<class Object> class List { private: s

STL 簡單紅黑樹的實現

1.紅黑樹簡介 二叉搜尋樹能夠提供對數的元素插入和訪問。二叉搜尋樹的規則是:任何節點的鍵值一定大於其左子樹的每一個節點值,並小於右子樹的每一個節點值。 常見的二叉搜尋樹有AVL-tree、RB-tree(紅黑樹)。紅黑樹具有極佳的增、刪、查效能,故我們選擇紅黑樹作為關聯式容

Python 使用list實現簡單的堆疊

#! /usr/bin/env python stack = [] def pushit(): stack.append(raw_input('Enter new string: ').strip()) def popit(): if len(stac

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 軟件來的流,並且可以通過網頁看出來就好。其實就是給老家的一個

簡單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&