【STL源码剖析】list模拟实现 | 适配器实现反向迭代器【超详细的底层算法解释】

今天博主继续带来STL源码剖析专栏的第三篇博客了!
今天带来list的模拟实现!
话不多说,直接进入我们今天的内容!


前言

那么这里博主先安利一下一些干货满满的专栏啦!

手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。

STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482


实现过程中要注意的一些点

STL的list底层是用带头循环双向链表实现的,里面的增删查改算法也是数据结构中算比较基础的操作了,如果对这些操作还不熟悉的,可以通过博主给的传送门先看看C语言实现的版本,了解好基本的算法操作,再回来食用这篇文章。

【链表】双向链表的介绍和基本操作(C语言实现)【保姆级别详细教学】

实现重点:迭代器和反向迭代器

关于迭代器和反向迭代器的具体实现,博主在代码的注释里面详细解释!

现在先列出一些STL迭代器的一些基本的知识点:

这个反向迭代器 -- list可以用 vector也可以用!(博主在代码中呈现)
什么容器的反向我们适配不出来?
map set是可以的,什么不可以呢
只要正向迭代器支持 ++ ,-- 都可以适配出来
++一般容器都有,--不一定有
比如单链表 -- forward_list就没有--
1.forward_list 单链表
2.unordered_map
3.unordered_set

迭代器从角度分类:
forward_iterator ++
bidirectional_iterator ++ --
random_access_iterator ++ -- + -  随机位置迭代器

deque vector 随机迭代器
map set list 双向迭代器
forward_list 单向迭代器

forward_iterator
bidirectional_iterator
random_access_iterator
其实它们是一种继承的关系
双向是特殊的单向,随机是特殊的双向

 MyList.h

#pragma once

#include<iostream>
#include<algorithm>
#include<cassert>
#include<list>
#include"reverse_iterator.h"

using namespace std;

namespace yufc {
	template<class T>
	struct list_node {
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;
		list_node(const T& x = T()) //给缺省
			:_data(x), _next(nullptr), _prev(nullptr)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct __list__iterator {
		typedef list_node<T>Node;
		//标准的stl迭代器要检查类型的
		//如果不加下面几句用不了find
		typedef bidirectional_iterator_tag iterator_category;
		typedef T value_type;
		typedef Ptr pointer;
		typedef Ref reference;
		typedef ptrdiff_t difference_type;

		Node* _node;
		__list__iterator(Node* node)//构造
			:_node(node) {}
		//核心控制行为
		bool operator!=(const __list__iterator& it)const {
			return _node != it._node;
		}
		//如果我们返回 const T& 我们就不能改了
		//对迭代器解引用 -- 注意,要返回引用 -- 可读可写
		Ref operator*() { // 同样,泛型化
			return _node->_data;
		}
		__list__iterator& operator++() {
			_node = _node->_next;
			return *this;
		}
		//继续完善
		bool operator==(const __list__iterator& it)const {
			return _node == it._node;
		}
		__list__iterator operator++(int) {
			__list__iterator tmp(*this);//先保存一下之前的值
			_node = _node->_next;
			return tmp;//只能传值返回了 -- 因为是临时对象
		}
		__list__iterator operator--(int) {
			__list__iterator tmp(*this);//先保存一下之前的值
			_node = _node->_prev;
			return tmp;//只能传值返回了 -- 因为是临时对象
		}
		__list__iterator& operator--() {
			_node = _node->_prev;
			return *this;
		}
		//stl里面还重载了一个 -> 毕竟迭代器就是[指针行为]的一种数据类型
		//什么时候会用 -> 呢? 数据类型是结构体(类的时候)就需要了
		//T* operator->() {
		Ptr operator->() { //泛型化 -- 你传什么类型就调用什么类型
			//重载不了 -- 因为返回值不同不构成重载
			return &(operator*());
		}
	};


	template<class T>
	class list {
		typedef list_node<T>Node;
	public:
		typedef __list__iterator<T, T&, T*>iterator;
		typedef __list__iterator<T, const T&, const T*>const_iterator;
		typedef __reverse_iterator<iterator, T&, T*>reverse_iterator;
		typedef __reverse_iterator<const_iterator, const T&, const T*>const_reverse_iterator;


		iterator begin() {
			return iterator(_head->_next);//begin()是哨兵位的下一个位置
		}
		iterator end() {
			return iterator(_head);//end()是哨兵位
		}
		reverse_iterator rbegin() {
			return reverse_iterator(end());//因为前面我们设计的是对称的,所以你的正就是我的反,你的反就是我的正
		}
		reverse_iterator rend() {
			return reverse_iterator(begin());
		}
		const_iterator begin() const {
			return const_iterator(_head->_next);//begin()是哨兵位的下一个位置
		}
		const_iterator end() const {
			return const_iterator(_head);//end()是哨兵位
		}
		list() {
			empty_init();
		}
		~list() {
			//如果我们写完析构 -- 再浅拷贝 -- 就会肯定会崩溃了
			//先clear一下,再把头弄掉就行了
			this->clear();
			delete _head;
			_head = nullptr;
		}
		void empty_init() {
			//创建并初始化哨兵位头节点
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}//这样写好之后我们的构造也直接调用这个empty_init()就行了
		//拷贝构造
		//现在写法 -- 复用构造函数 -- 这个构造函数是传一个迭代器区间的(不一定要是list的迭代器)
		template<class InputIterator>
		list(InputIterator first, InputIterator last) { //构造
			empty_init();//创建并初始化哨兵位头节点
			while (first != last) { //当然我们不能直接插入 -- 头节点要先弄好 -- 不然直接push直接崩
				push_back(*first);
				++first;
			}
		}
		void swap(list<T>& x) {
			std::swap(_head, x._head);
		}
		list(const list<T>& lt) {
			//直接复用list(InputIterator first, InputIterator last)构造函数就行
			//lt2(lt1)
			empty_init();//先把自己初始化一下
			list<T>tmp(lt.begin(), lt.end());//这个构造结果就是lt2想要的
#if 0
			std::swap(_head, tmp._head);//直接换头指针就行了
#endif
			swap(tmp);
		}
		list<T>& operator=(list<T> lt) {
			//lt是一个深拷贝临时对象,换过来 -- 还帮你释放
			swap(lt);
			return*this;
		}
		void clear() {
			//清数据 -- 哨兵位是要保留的
			iterator it = begin();
			while (it != end()) {
				it = erase(it);
				//erase之后 -- 就失效了 -- 但是会返回下一个位置的迭代器
				//所以it = erase(it) 这样写就行了
			}
		}
		void push_back(const T& x) {
#if 0
			Node* tail = _head->_prev;//尾节点
			Node* newnode = new Node(x);
			//简单的链接关系
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
#endif
			//复用insert
			insert(end(), x);
		}
		void push_front(const T& x) {
			insert(begin(), x);
		}
		iterator insert(iterator pos,const T&x){
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			//prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;

			return iterator(newnode);
		}
		void pop_back() {
			erase(--end());
		}
		void pop_front() {
			erase(begin());
		}
		iterator erase(iterator pos) {
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);//返回下一个位置的迭代器
		}
	/// <summary>
	/// 迭代器 -- 重点之重点
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// 我们怎么用++去调用迭代器呢
	/// C++的两个精华 -- 封装、运算符重载/
	/// 首先肯定要重载*,要重载++
	private:
		Node* _head;
	};


	void print_list(list<int>&lt) {
		for (auto e : lt) {
			cout << e << " ";
		}
		cout << endl;
	}
	void test() {
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		lt.push_front(0);
		lt.push_front(-1);

		list<int>::iterator it = lt.begin();
		while (it != lt.end()) {
			cout << *it << " ";
			++it;
		}
		cout << endl;
		for (auto e : lt) {//相应的,它也能跑了
			cout << e << " ";
		}
		cout << endl;
		lt.pop_back();
		lt.pop_back();
		for (auto e : lt) {//相应的,它也能跑了
			cout << e << " ";
		}
		cout << endl;
		lt.pop_front();
		lt.pop_front();
		for (auto e : lt) {//相应的,它也能跑了
			cout << e << " ";
		}
		cout << endl;

		//find
		//在3之前插入一个30
		auto pos = find(lt.begin(), lt.end(), 3);
		if (pos != lt.end()) {
			//pos会失效吗?不会
			lt.insert(pos, 30);
			*pos *= 100; // 迭代器指向的是3
		}
		for (auto e : lt) {
			cout << e << " ";
		}
		cout << endl;
	}

	void func(const list<int>& lt) {
		//现在想去遍历一下
		//肯定是跑不了的 -- 需要const迭代器
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end()) {
			//*it = 10;
			cout << *it << " ";
			++it;
		}
	}
	void test2() {
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		func(lt);
	}

	void test3() {
		//深浅拷贝问题
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		list<int>copy = lt;
		*lt.begin() = 10;
		print_list(lt);
		cout << endl;
		print_list(copy);
		//如果是浅拷贝的话两个都改了 -- 两个list的头都指向同一个哨兵位头节点 -- 同一个链表
		lt.clear();
		cout << endl;
		print_list(lt);
		lt.push_back(1);
		print_list(lt);
	}
	void test4() {
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		list<int>copy;
		copy = lt;
		print_list(lt);
		print_list(copy);
		*lt.begin() = 10;
		print_list(lt);
		print_list(copy);
	}

	//反向迭代器测试
	void test5() {
		list<int>lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		lt.push_back(6);
		lt.push_back(7);
		lt.push_back(8);
		print_list(lt);
		//反向迭代器测试
		list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend()) {
			cout << *rit << " ";
			++rit;
		}
		cout << endl;
	}
}
//现在要去实现反向迭代器
//普通思维:拷贝一份正向迭代器 -- 修改一下
//大佬思维:
//1.list可以这样实现,那vector那些的怎么办呢?
//	vector也可以像list一样弄一个struct 去重载
//  原来vector迭代器直接++是不用operator的,因为本来这个行为就可以符合规范
//	但是现在我们需要实现一个vector的反向迭代器,我们就可以operator一个++ 实际上是--_ptr
//2.复用! -- 见vector的新头文件

reverse_iterator.h

namespace yufc {
	//复用,迭代器适配器
	template<class Iterator, class Ref, class Ptr>
	struct __reverse_iterator {
		Iterator _cur;
		typedef __reverse_iterator<Iterator, Ref, Ptr> RIterator;
		__reverse_iterator(Iterator it)
			:_cur(it) {}
		//为了对称,stl源码进行了一些操作
		RIterator operator++() { //迭代器++,返回的还是迭代器
			--_cur;//反向迭代器++,就是封装的正向迭代器--
			return *this;
		}
		RIterator operator--() {
			++_cur;//反向迭代器++,就是封装的正向迭代器--
			return *this;
		}
		Ref operator*() {
			//return *_cur;
			//为什么这里需要拷贝一下对象呢?
			//因为解引用只是取一下数据,迭代器位置肯定是不能变的 -- 变了肯定会出问题的
			auto tmp = _cur;
			--tmp;
			return *tmp;
		}
		Ptr operator->() {
			return &(operator*());
		}
		bool operator!=(const RIterator& it) {
			return _cur != it._cur;
		}
	};
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

//总结:适配器的特点就是 -- 复用!!!

#include"MyList.h"

#if 0
// 正向打印链表
template < class T>
void PrintList(const yufc::list<T>&l)
{
	auto it = l.cbegin();
	while (it != l.cend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
// 测试List的构造
void TestList1()
{
	yufc::list<int> l1;
	yufc::list<int> l2(10, 5);
	PrintList(l2);
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	yufc::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
	PrintList(l3);
	yufc::list<int> l4(l3);
	PrintList(l4);
	l1 = l4;
	PrintList(l1);
	PrintListReverse(l1);
}
// PushBack()/PopBack()/PushFront()/PopFront()
void TestList2()
{
	// 测试PushBack与PopBack
	yufc::list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	PrintList(l);
	l.pop_back();
	l.pop_back();
	PrintList(l);
	l.pop_back();
	cout << l.size() << endl;
	// 测试PushFront与PopFront
	l.push_front(1);
	l.push_front(2);
	l.push_front(3);
	PrintList(l);
	l.pop_front();
	l.pop_front();
	PrintList(l);
	l.pop_front();
	cout << l.size() << endl;
}
void TestList3()
{
	int array[] = { 1, 2, 3, 4, 5 };
	bite::list<int> l(array, array + sizeof(array) / sizeof(array[0]));
	auto pos = l.begin();
	l.insert(l.begin(), 0);
	PrintList(l);

	++pos;
	l.insert(pos, 2);
	PrintList(l);
	l.erase(l.begin());
	l.erase(pos);
	PrintList(l);
	// pos指向的节点已经被删除,pos迭代器失效
	cout << *pos << endl;
	auto it = l.begin();
	while (it != l.end())
	{
		it = l.erase(it);
	}
	cout << l.size() << endl;
}
#endif


#if 0
int main() {
	//yufc::test();
	//yufc::test2();
	//yufc::test3();
	//yufc::test4();
	yufc::test5();
	return 0;
}
#endif



//forward_iterator
//bidirectional_iterator
//random_access_iterator
//其实它们是一种继承的关系
//双向是特殊的单向,随机是特殊的双向

//源码其实比我们写的版本还复杂得多
//很多版本控制
//迭代器萃取那些也可以了解一下
//萃取 -- 可以提高效率
//比如迭代器要相减
//萃取技术(很复杂的一个技术)可以萃取出迭代器的类型
//比如:如果是随机迭代器类型就直接相减,如果是双向迭代器就要不断++算距离
//萃取就提高了效率

尾声

看到这里,相信大家对list类的模拟实现已经有一定的了解了!list的模拟实现,是我们掌握STL的开始,后面,博主将会给大家带来stack等等STL容器的模拟实现,持续关注,订阅专栏,点赞收藏都是我创作的最大动力。

转载时请注明作者和出处。未经许可,请勿用于商业用途 )
更多文章请访问我的主页

@背包https://blog.csdn.net/Yu_Cblog?type=blog

posted @ 2022-10-11 11:04  背包Yu  阅读(7)  评论(0编辑  收藏  举报  来源