【C++自学笔记】STL-list的深度剖析及模拟实现(源码)

一、list的介绍及使用

1、list 的介绍

  • list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代;
  • list 底层时双向链表结构,双向链表中每个元素存储在互不想关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素;
  • list 和 forward_list 非常相似,主要不同在于 forward_list 是单链表,只能朝前迭代,已让其简单高效;
  • 与其他的序列式容器相比较,list 通常在任意位置进行插入,移除元素的执行效率更好;
  • 与其他的序列式容器相比,list 和 forward_list 最大缺陷就是不支持在任意位置的随机访问;

2、list的使用

构造函数 接口说明
list()

构造空的list

list(size_type n,const value_type* val = value_type()) 构造的list中包含n个值为val的元素
list(const list&x) 拷贝构造函数
list(Inputlterator firs,Inputlterator last) 用[list,last)区间中的元素构造
void MakeListTest() {
	list l1;                       //空    
	list l2(4,100);                //l2中放置4个值为100的元素
	list l3(l2.begin(),l2.end());  //用l2中的[begin,end)区间构造l3
	list l4(l3);                   //用l3拷贝构l4

	//以数组为迭代器区间构造l5
	int array[] = {9, 5, 2, 7};
	list l5(array,array+sizeof(array)/sizeof(int));

	//使用迭代器的当时打印l5
	for (list::iterator it = l5.begin(); it != l5.end();++it) {
		cout << *it << " ";
	}
	cout << endl;

	for (auto x : l5)
		cout << x << " ";
	cout << endl;

3、list iterator的使用

此处,可以暂时把迭代器理解为一个指针,该指针指向 list 中的某个节点;

函数声明 接口说明
begin()

返回第一个元素的迭代器

end() 返回最后一个元素下一个位置的迭代器
rbegin() 返回第一个元素的reverse_iterator,及end位置
rend() 返回最后一个元素下一个位置reverse_iterator,即begin位置

cbegin()

返回第一个元素的const_iterator(C++11)

cend()

但回最后一个元素下一个位置的const_iterator(C++11)
crbegin() 即credn()的位置(C++11)
crend() 即crbegin()的位置(C++11)
void IteratorListTest() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
	list ll(array, array + sizeof(array) / sizeof(int));

	//正向迭代器
	for (list::iterator it = ll.begin(); it != ll.end();++it) {
		cout << *it << " ";
	}
	cout << endl;
	//反向迭代器
	for (list::reverse_iterator it = ll.rbegin(); it != ll.rend();++it) {
		cout << *it << " ";
	}
	cout << endl;
	//const 正向迭代器(C++11)
	auto cit = ll.cbegin();
	cout << typeid(cit).name() << endl;
	//const 反向迭代器(C++11)
	auto crit = ll.crbegin();
	cout << typeid(crit).name() << endl;
}

总结:

  1. begin 与 end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动;
  2. rbegin 与 rend 为反相迭代器,对迭代器执行 ++ 操作,迭代器向前移动;
  3. cbegin 与 cend 为 const 的正向迭代器,与 begin 和 end 不同的是:该迭代器指向节点中的元素值不能修改;
  4. crbegin 与 crend 为 const 的反向迭代器,与 rbegin 和 rend 不同的是:该迭代器指向节点中的元素值不能修改;

4、list capacity

函数声明 接口说明
bool empty() const 检测list是否为空,是返回true,否则返回false
size_t size() const 返回list中有效节点的个数

 

void ListCapacityTest() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list ll(array, array + sizeof(array) / sizeof(int));

	//打印list 中有效节点的个数
	cout << ll.size() << endl;

	//检测list 是否为空
	if (ll.empty())
		cout << "empty()" << endl;
	else
		for (auto a : ll)
			cout << a << " ";
		cout << endl;
}

5、list element access

函数声明 接口说明
reference front() 返回list的第一个节点中值的引用
const_reference front() const 返回list的第一个节点中值的const引用

reference back()

返回list的最后一个节点中值的引用
const_reference back() const 返回list的最后一个节点中值的const引用

 

void ListElementAccessTest() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list ll(array, array + sizeof(array) / sizeof(int));

	for (auto a : ll)
		cout << a << " ";
	cout << endl;

	//将list 中的第一个节点的值 和 最后一个节点的值 改为 20;
	ll.front() = 20;
	ll.back() = 20;

	for (auto a : ll)
		cout << a << " ";
	cout << endl;

	const list ll2(array,array+sizeof(array)/sizeof(int));
	const int& ca = ll2.front();

}

6、list modifiers

函数声明 接口说明
void push_front(const value_type& val) 头插
void pop_front() 头删
void push_back(const value_type& val) 尾插
void pop_back() 尾删

template

void empalce_front(Args&&.. args)(C++11)

在list的第一个元素前根据参数直接构造元素

tmplate

void emplace_back(Args&&.. args) (C++11)

在list的最后一元素后位置根据参数直接构造元素
iterator insert(iterator pos,const value_type& val) 在pos位置插入值为 val 的元素
void insert(iterator pos,size_type n,const value_type& val) 在pos为插入 n 个值为val 的元素
void insert(iterator pos,Inputlterator firsr,inputlterator last) 在pos为插入[first,last)区间中的元素
iterator erase(iterator pos) 删除pos位置的元素
iterator erase(iterator first,iterator last) 删除[first,list)中的元素
void swap(list& x) 交换两个list中的元素
void resize(size_type n,value_type val = value_type()) 将list 中有效元素个数改变为n,多出的元素用val 填充
void clear() 清空list 所有元素

测试==》》

1、push_front / pop_front / push_back / pop_back

void ListModifiersTest() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list L(array, array + sizeof(array) / sizeof(int));

	// push_front / pop_front / push_back / pop_back
	L.push_front(6);
	L.push_back(6);
	Print(L);

	L.pop_front();
	L.pop_back();
	Print(L);

}

2、empalce_back / emplace_front / emplace

void ListModifiersTest() {
	//emplace_back / emplace_front / empalce
	list l;
	Date d(2018, 1, 1);
	l.push_back(d);
	l.emplace_back(2012, 12, 12);
	l.emplace_front(2019, 12, 12);

}

【C++自学笔记】STL-list的深度剖析及模拟实现(源码)_第1张图片 

3、insert / erase

void ListModifiersTest() {
	//insert / erase
	int array[] = { 1, 2, 3};
	list L1(array, array + sizeof(array) / sizeof(int));
	//获取链表的第二个结点
	auto pos = ++L1.begin();
	cout << *pos << endl;
	//在pos的前面插入4
	L1.insert(pos, 4);
	Print(L1);
	//在pos的前插入4个5
	L1.insert(pos, 4, 5);
	Print(L1);
	//在pos前面插入[v.begin(),v.end())区间中的的元素
	vector v{9,5,2,7};
	L1.insert(pos, v.begin(), v.end());
	Print(L1);


	//删除pos位置的元素
	L1.erase(pos);
	Print(L1);
	//删除list 中[begin,end)区间的中的所有元素
	L1.erase(L1.begin(), L1.end());
	Print(L1);


}

【C++自学笔记】STL-list的深度剖析及模拟实现(源码)_第2张图片

4、resize / swap / clear

void Test() {
	int array[] = { 1, 2, 3 };
	list L(array, array + sizeof(array) / sizeof(int));
	Print(L);
	
	//L的元素增加为10个,多余的默认值填充
	L.resize(10);
	Print(L);
	//L的元素增加为20个,多余的用1填充
	L.resize(20, 1);
	Print(L);
	//L的元素减少到5个
	L.resize(5);
	Print(L);
	//用vector中的元素来构造list
	vector v{ 9,5,2,7 };
	list ll(v.begin(), v.end());
	Print(ll);
	//交换L和ll
	L.swap(ll);
	Print(L);
	Print(ll);
	//清空ll
	Print(ll);

}

【C++自学笔记】STL-list的深度剖析及模拟实现(源码)_第3张图片

7、list 迭代器失效情况

迭代器失效即迭代器所指向的节点的无效,即该节点被删除。因为list 的底层结构均为带头节点的双向链表结构,因此在list 中插入时是不会导致list 的迭代器失效的,只有删除的时候才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响;

void ListIteratorError() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list ll(array, array + sizeof(array) / sizeof(int));

	auto it = ll.begin();
	while (it != ll.end()) {
		ll.erase(it);
		++it;
	}
}

注:erase()执行后,it 所指向的节点已经删除,因此 it 无效,在下一次使用 it 时,必须先给其赋值;

二、list 与 vector 的对比

vector 和 list 都时 STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同;

1、底层结构:

  • vector:动态顺序表,一段连续的空间;
  • list:带头结点的双向循环链表;

2、随机访问:

  • vector:支持随机访问,访问某个元素效率O(1);
  • list:不支持随机访问,访问某个元素的效率为O(N);

3、插入和删除:

  • vector:任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要扩容(开辟新空间,拷贝元素,释放旧空间,导致效率更低);
  • list:任意位置的插入和删除效率极高,不需要搬移元素,时间复杂度为O(1);

4、空间利用率:

  • vector:底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高;
  • list:底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低;

5、迭代器:

  • vector:原生态指针;
  • list:对原生态指针(节点指针)进行封装;

6、迭代器失效:

  • vector:在插入元素的时候,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来的迭代器失效,删除时,当前迭代器需要重新赋值否则会失效;
  • list:插入元素不会导致迭代器的失效,删除元素的时候,也只会导致当前的迭代器失效,其他迭代器不会受影响;

7、使用场景:

  • vector:需要高效存储,支持随机访问,不关新插入、删除的效率;
  • list:大量插入和删除操作,不关心随机访问;

三、list 的模拟实现

有点长。。。

#include
using namespace std;
namespace My {
	template
	struct ListNode{
		ListNode(const T& val = T())
			:_pPre(nullptr)
			, _pNext(nullptr)
			, _val(nullptr)
		{}
		ListNode* _pPre;
		ListNode* _pNext;
		T _val;
	};
	//正向迭代器
	template
	class ListIterator {
		typedef ListNode* PNode;
		typedef ListIterator Self;
	public:
		ListIterator(PNode pNode = nullptr)
			:_pNode(pNode)
		{}
		ListIterator(const Self& l)
			:_pNode(l._pNode) 
		{}
		T* operator*() {
			return _pNode->_val;
		}
		T* operator->() {
			return &(operator*());
		}
		Self& operator++() {
			_pNode = _pNode->_pNext;
			return *this;
		}
		Self& operator++(int) {
			Self temp(*this);
			_pNode = _pNode->_pNext;
			return temp;
		}
		bool operator==(const Self& l) {
			return _pNode == l._pNode;
		}
		bool operator!=(const Self& l) {
			return _pNode != l._pNode;
		}
		PNode _pNode;
	};
	//反向迭代器
	template
	class ListReverseIterator {
		typedef ListReverseIterator Self;
	public:
		ListReverseIterator(const Iterator& it)
			:_it(it)
		{}
		ListReverseIterator(const Self& l)
			:_it(l._it)
		{}
		Ref operator*() {
			Iterator temp = _it;
			return *(--temp);
		}
		Ref operator->() {
			return &operator*();
		}
		Self& operator++() {
			--_it;
			return *this;
		}
		Self operator++() {
			Iterator temp(_it);
			--_it;
			return temp;
		}
		Self& operator--() {
			++_it;
			return *this;
		}
		Self operator--() {
			iterator temp(_it);
			++_it;
			return temp;
		}
		bool operator!=(const Self& s) {
			return _it != s._it;
		}
		bool operator==(const Self& s) {
			return _it == s._it;
		}
	private:
		Iterator _it;
	};
	template
	class List {
		typedef ListIterator Iterator;
		typedef ListIterator ConstIterator;
		typedef ListReverseIterator ReverseIterator;
		typedef ListReverseIterator ConstReveresIterator;
	public:
		//构造
		List() {
			CreateHead();
		}
		List(int n ,const T& value = T()) {
			CreateHead();
			for (int i = 0; i < n;++i) {
				PushBack(value);
			}
		}
		template
		List(Iterator first,Iterator last) {
			CreateHead();
			while (first!=last) {
				PushBack(*first);
				++first;
			}
		}
		List(const List& l) {
			CreateHead();
			List temp(l.CBegin(), l.CEnd());
			this->Swap(temp);
		}
		List& operator=(const List& l) {
			if (this != &l) {
				List temp(l);
				this->Swap(temp);
			}
			return *this;
		}
		~List() {
			Clear();
			delete _PHead;
			_PHead = nullptr;
		}
		/*    List Itreator   */
		Iterator Begin() {
			return Iterator(_PHead->_pNext);
		}
		Iterator End() {
			return Iterator(_PHead);
		}
		ReverseIterator RBegin() {
			return ReverseIterator(End());
		}
		ReverseIterator REnd() {
			return ReverseIterator(Begin());
		}
		ConstIterator CBegin() {
			return ConstIterator(_PHead->_pNext);
		}
		ConstIterator CEnd() {
			return ConstIterator(_PHead);
		}
		ConstReveresIterator CRBegin() {
			return ConstReveresIterator(CEnd());
		}
		ConstReveresIterator CREnd() {
			return ConstReveresIterator(CBegin());
		}
		/*    List Capacity   */
		size_t Size()const {
			size_t count = 0;
			PNode pCur = _PHead->_pNext;
			while (pCur != _PHead) {
				++count;
				pCur = pCur->_pNext;
			}
			return count;
		}
		bool Empty() {
			return _PHead->_pNext == _PHead;
		}
		void Resize(size_t newSize,const T& val = T()) {
			size_t oldSize = Size();
			if (oldSize <= newSize) {
				for (size_t i = oldSize; i < newSize; ++i)
					PushBack(val);
			}
			else {
				for (size_t i = newSize; i < oldSize; ++i)
					PopBack(val);
			}
		}
		/*    List Access   */
		T& Front() {
			return _PHead->_pNext->_val;
		}
		const T& Front()const {
			return _PHead->_pNext->_val;
		}
		T& Back() {
			return _PHead->_pPre->_val;
		}
		const T& Back() const {
			return _PHead->_pPre->_val;
		}
		/*    List Modify   */
		void PushBack(const T& val) {
			PNode pNewNdoe = new Node(val);

			pNewNdoe->_pNext = _PHead;
			pNewNdoe->_pPre = _PHead->_pPre;

			_PHead->_pPre = pNewNdoe;
			pNewNdoe->_pPre->_pNext = pNewNdoe;
		}
		void PopBack() {
			PNode pDel = _PHead->_pPre;
			if (pDel != _PHead) {
				_PHead->_pPre = pDel->_pPre;
				pDel->_pPre->_pNext = _PHead;
				delete pDel;
			}
		}
		void PushFront(const T& val) {
			PNode pNewNode = new Node(val);

			pNewNode->_pNext = _PHead->_pNext;
			pNewNode->_pPre = _PHead;

			_PHead->_pNext = pNewNode;
			pNewNode->_pNext->_pPre = pNewNode;
		}
		void PopFront() {
			PNode PDel = _PHead->_pNext;
			if (PDel != _PHead) {
				_PHead->_pNext = PDel->_pNext;
				PDel->_pNext->_pPre = _PHead;
				delete PDel;
			}
		}
		Iterator Inerst(Iterator pos,const T& val) {
			PNode pNewNode = new Node(val);
			PNode pCur = pos._pNode;

			pNewNode->_pPre = pCur->_pPre;
			pNewNode->_pNext = pCur;
			pNewNode->_pPre->_pNext = pNewNode;
			pCur->_pPre = pNewNode;
			return iterator(pNewNode);
		}
		Iterator Erase(Iterator pos) {
			PNode pDel = pos._pNode;
			PNode pRet = pDel->_pNext;

			pDel->_pPre->_pNext = pDel->_pNext;
			pDel->_pNext->_pPre = pDel->_pPre;
			delete pDel;

			return Iterator(pRet);
		}
		void Clear() {
			PNode pCur = _PHead->_pNext;
			while (pCur != _PHead) {
				_PHead->_pNext = pCur->_pNext;
				delete pCur;
				pCur = _PHead->_pNext;
			}
			_PHead->_pNext = _PHead;
			_PHead->_pPre = _PHead;
		}
		void Swap(List& l) {
			swap(_PHead,l._PHead);
		}
	private:
		void CreateHead() {
			_PHead = new Node;
			_PHead->_pPre = _PHead;
			_PHead->_pNext = _PHead;
		}
	private:
		PNode _PHead;
	};
}

 

你可能感兴趣的:(【C/C++】)