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++ STL之List實現
概述 List是STL中常用的容器之一,List將元素按順序儲存到連結串列中,在快速刪除和快速插入方面比vector高出許多。STL中的list是一雙向連結串列,具有指向前一節點和後一節點的指標。那麼我們開始寫一個屬於自己的List吧。 list的節點
STL中list實現降序排列
STL list可以對基本資料、字串直接呼叫sort()函式預設做升序排列,但是對於降序排列或者對非基本資料型別排序(物件、陣列等)需要藉助operator()函式實現,這點和Java中的List很相似。 具體呼叫函式: list.sort(Cmpare())
【STL】list的簡單剖析以及各種函式的實現
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
stl中list的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&