[C++]一文带你理解STL的list

目录

一.模拟代码实现

二.代码具体分析

2.1 节点类的模拟实现

2.2 迭代器类的模拟实现

2.2.1 迭代器构造函数

2.2.2 ++运算符的重载

2.2.3 --运算符的重载

2.2.4 !=和==运算符的重载

2.2.5 *运算符的重载

2.2.6 ->运算符的重载

2.3 list的模拟实现

2.3.1 构造函数

2.3.2 拷贝构造函数

2.3.3 赋值运算符重载函数

2.3.4 clear函数

2.3.5 析构函数

2.3.6 迭代器相关函数 

2.3.7 插入、删除函数 

头插、头删、尾插、尾删

 2.3.8 size()


一.模拟代码实现

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>
#include<assert.h>
#include<functional>
using namespace std;


namespace rzp
{
	//很明显,这个是一个节点的结构
	template <class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(val)
		{

		}
	};
	//这个对应的是迭代器的应用
	//typedef _list_iterator<T, T&, T*> iterator;
	//typedef _list_iterator<T, const T&, const T*> const_iterator;
	template<class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> self;
		Node* _node;

		_list_iterator(Node* node)
			:_node(node)
		{
		}
		//拷贝构造、operator=、析构我们不写 编译器默认生成就可以用
		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			//很明显,这里是指向节点中的数据
			//return &(operator*());
			return &_node->_data;
		}

		//前置++
		//++it ->it.operator++(&it)
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		//it++ -> it.operator++(&it,0)
		//因为后置++  其中的值并没有改变
		//所以这里对应的是传值
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		//这块地方的重载也的确是用节点的指针进行比较
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}

		bool operator==(const self& it) const
		{
			return _node == it._node;
		}

	};
	//这个对应的才是整个链表的结构
	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 _list_const_iterator<T> const_iterator;

		iterator begin()
		{
			return iterator(_head->_next);
			//return _head->_next;
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		//copy(lt)
		list(const list<T>& lt)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}
		//析构函数是整个都要销毁

		//对应赋值的用法
		//list<T>& operator=(const list<T>& lt)
		//{
		//	if (this != &lt)
		//	{
		//		clear();
		//		for (const auto& e : lt)
		//		{
		//			push_back(e);
		//		}
		//	}
		//	return *this;
		//}

		list<T>& operator=(list<T> lt)
		{
			swap(_head, lt._head);
			return *this;
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		//而clear则只是销毁掉有效数据
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
			}
		}
		//头节点并不会被销毁

		//链表的拷贝和构造函数
		void push_back(const T& x)
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);

			 _head       tail  newnode
			//tail->_next = newnode;
			//newnode->_prev = tail;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		void insert(iterator pos, const T& x)
		{
			assert(pos._node);
			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;
		}

		//关键还是它们之间的链接关系
		iterator erase(iterator pos)
		{
			assert(pos._node);
			assert(pos != end());
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;

			delete pos._node;
			prev->_next = next;
			next->_prev = prev;

			return iterator(next);
		}
		//iterator erase(iterator pos)

		bool empty()
		{
			return begin() == end();
		}
		//对应的size()结构
		size_t size()
		{
			size_t sz = 0;
			iterator it = begin();
			while (it != end())
			{
				++sz;
				++it;
			}
			return sz;
		}
	private:
		Node* _head;
	};
}

二.代码具体分析

2.1 节点类的模拟实现

这个节点其实就是一个双向带头循环的链表结构,代码实现如下:

template <class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(val)
		{

		}
	};

 注意:当构造节点并没有传递数据的时候,则默认以缺省参数为值传入数据.

2.2 迭代器类的模拟实现

//这个对应的是迭代器的应用
	//typedef _list_iterator<T, T&, T*> iterator;
	//typedef _list_iterator<T, const T&, const T*> const_iterator;
	template<class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> self;
		Node* _node;

		_list_iterator(Node* node)
			:_node(node)
		{
		}
		//拷贝构造、operator=、析构我们不写 编译器默认生成就可以用
		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			//很明显,这里是指向节点中的数据
			//return &(operator*());
			return &_node->_data;
		}

		//前置++
		//++it ->it.operator++(&it)
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		//it++ -> it.operator++(&it,0)
		//因为后置++  其中的值并没有改变
		//所以这里对应的是传值
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		//这块地方的重载也的确是用节点的指针进行比较
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}

		bool operator==(const self& it) const
		{
			return _node == it._node;
		}

	};

对于list来说,其各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作。迭代器让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行访问。

其实就是将在vector和string类的节点指针迭代器进行了封装,并对其各种运算符进行了重载,使得迭代器的各种操作和指针一致.

template<class T, class Ref, class Ptr>

这里:Ref代表引用类型,Ptr代表指针类型.

若该迭代器不设计三个模板参数,那么就不能很好的进行区分普通迭代器和const迭代器.

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

很明显,普通迭代器是可以对节点数据进行改变的,const迭代器限制了节点数据的改变.

2.2.1 迭代器构造函数

        _list_iterator(Node* node)
 			:_node(node)
		{
		}

2.2.2 ++运算符的重载

//前置++
		//++it ->it.operator++(&it)
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		//it++ -> it.operator++(&it,0)
		//因为后置++  其中的值并没有改变
		//所以这里对应的是传值
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

其实.这里的前置++和后置++,这里和vector中的类型极其相似,前置++等同于传引用,因为要改变值;后置++则等同于传值,创建一个临时变量tmp,将节点中的_node改变之后,传出去的是tmp这个临时变量.

2.2.3 --运算符的重载

        self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

--运算符的重载和++运算符的重载极其的类似.

2.2.4 !=和==运算符的重载

        //这块地方的重载也的确是用节点的指针进行比较
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}

		bool operator==(const self& it) const
		{
			return _node == it._node;
		}

这里就是对节点的_node进行比较即可,_node中的_prev和_next和_data进行比较.

2.2.5 *运算符的重载

        Ref operator*()
		{
			return _node->_data;
		}

*运算符得到该位置的数据内容,并且返回引用.

2.2.6 ->运算符的重载

对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可。

        Ptr operator->()
		{
			//很明显,这里是指向节点中的数据
			//return &(operator*());
			return &_node->_data;
		}

这里返回的是ptr也就是对应节点的指针. 

这里本来是应该有两个->的,第一个箭头是pos ->去调用重载的operator->返回Date* 的指针,第二个箭头是Date* 的指针去访问对象当中的成员变量_year。但是一个地方出现两个箭头,程序的可读性太差了,所以编译器做了特殊识别处理,为了增加程序的可读性,省略了一个箭头。

2.3 list的模拟实现

2.3.1 构造函数

list是一个带头双向循环链表,在构造一个list对象时,申请一个头节点,并让其_prev和_next都指向同一个节点.

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;
     list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
     private:
         Node* _head;
     } 

2.3.2 拷贝构造函数

		//copy(lt)
		list(const list<T>& lt)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

2.3.3 赋值运算符重载函数

list<T>& operator=(list<T> lt)
		{
			swap(_head, lt._head);
			return *this;
		}

这里直接利用编译器的机制,通过编译器自动调用list的拷贝构造函数,然后利用swap函数将原容器与该list对象进行交换即可

2.3.4 clear函数

        void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it=erase(it);
			}
		}

这个很明显就是清除掉容器当中的数据,调用erase进行清除容器中的内容, erase(it)之后return下一个位置的节点.还有另外一种写法如下:

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
			}
		}

这个写法其实和上面的写法基本一致,程序首先执行erase(it)的操作,但这时it指向的节点并没有发生改变,然后it++就指向了下一个节点.

2.3.5 析构函数

首先调用clear函数清理容器当中,然后将头节点释放,最后将头指针置空即可.

		~list()
		{
			clear();
 
			delete _head;
			_head = nullptr;
		}

2.3.6 迭代器相关函数 

begin和end:begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。即begin指向首元素,end指向尾后。

对于list这个带头双向循环链表来说:

对应迭代器的封装:

        iterator begin()
		{
			return iterator(_head->_next);
			//return _head->_next;
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

2.3.7 插入、删除函数 

insert(前插)

        void insert(iterator pos, const T& x)
		{
			assert(pos._node);
			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;
		}

这个主要也就是对应的插入函数之间的关系,建立节点之间的关系.

erase(删除)

iterator erase(iterator pos)
		{
			assert(pos._node);
			assert(pos != end());
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;

			delete pos._node;
			prev->_next = next;
			next->_prev = prev;

			return iterator(next);
		}

erase函数返回删除节点的下一个节点_next. 

头插、头删、尾插、尾删

这四个函数都可以关联对应的erase和insert函数进行实现:

        //链表的拷贝和构造函数
        //尾插的函数结构
		void push_back(const T& x)
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);

			 _head       tail  newnode
			//tail->_next = newnode;
			//newnode->_prev = tail;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			insert(end(), x);
		}
        //头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
        //尾删
		void pop_back()
		{
			erase(--end());
		}
        //头删
		void pop_front()
		{
			erase(begin());
		}

 2.3.8 size()

size函数显示list节点的个数:

        size_t size()
		{
			size_t sz = 0;
			iterator it = begin();
			while (it != end())
			{
				++sz;
				++it;
			}
			return sz;
		}


 

.


 

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡须不排序H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值