
🪐🪐🪐欢迎来到程序员餐厅💫💫💫
今日主菜:vector类
主厨:邪王真眼
          所属专栏:c++专栏
           主厨的主页:Chef‘s blog
总用光环在陨落,总有新星在闪烁
【本节目标】
1. 节点
2.迭代器
3.list
4. list与vector的对比
前言:
这次的list类较vector和string会更复杂,因此我们将他拆分为节点的类,迭代器的类,以及list类主体三点进行讲解。
一. list的介绍
- 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- 2. list的底层是双向带头循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- 3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素
 
 
二.节点
要点须知:
- 使用struct,标明公有属性(这样从外部调用比较方便)
- list是带头双向循环链表
template<class T>
struct ListNode//驼峰式命名
{
	ListNode<T>* _prev;
	ListNode<T>* _last;
	T _val;
	ListNode(const T &val=T())//缺省参数
		:_prev(nullptr)
	    ,_last(nullptr)
		,_val(val)
	{}
};二、迭代器
由于list的每个结点物理空间不连续,导致迭代器不能像之前string、vector那样简单的设计为指针,而是设计为一个类(进行封装),以此完成*、->、++、–-等一系列操作。
2.1 成员变量与默认构造函数
注意事项:
仍然使用struct,标明公有属性成员变量是一个结点的指针
template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;//为了方便使用节点类,我们给它重命名一下
	typedef ListIterator<T, Ref,  Ptr>self;//原理同上
	ListIterator(Node* ptr)
		:Node(ptr)
	{}
	Node* _Node;
};我知道你现在很疑惑为什么模版要用到三个参数,别急,等会讲解。
这里的迭代器的实际不可谓不妙,后面你就会称赞它的
2.2 operator*
Ref operator*()
{
	return _Node->_val;
}解释:对比一下以前的代码

2.3 operator->
Ptr operator->()
{
	return &_Node->val;
 }同样的的道理,这就是为什么迭代器的模板参数有三个。
这里的返回值是数据val类型的指针,分为const和非const
2.4 operator++
注意事项:
- 为了区分前置和后置,后置参数加上int(无实际意义,以示区分)
- 前置传引用返回,后置传值返回
self& operator++()
{
	_Node = _Node->_last;
	return _Node;
}
self operator++(int)
{
	Node* tmp = _Node;
	_Node = _Node->last;
	return tmp;
}2.5 operator- -
与++同理
self operator--(int)
{
	Node* tmp = _Node;
	_Node = _Node->_prev;
	return tmp;
}
self operator--()
{
	_Node = _Node->_prev;
	return _Node;
}2.6operator==
bool operator==(self& s)
{
	return _Node == s._Node;
}2.6operator!=
	bool operator!=(self& s)
	{
		return _Node != s._Node;
	}三、list
3.1 成员变量
注意事项:
head是哨兵位
template<class T>
class List
{
public:
	typedef ListNode Node;
private:
	Node* _head;
};3.2对迭代器的处理


typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&,const T*> const_iterator;- 1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- 2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动我们现在只实现begin和end
3.2.1begin()
注意事项:
- 1.返回值类型要强制类型转换
- 2.返回的是_head的下一个,不是_head,_head是哨兵位
iterator begin()
{
	return iterator(_head->_last);
}
const_iterator begin()const
{
	return const_iterator(_head->_last);
}3.2.2 end
细节:
1.返回值类型要强制类型转换
2.返回的是_head
iterator end()
{
	return iterator(_head);
}
const_iterator end()const
{
	return const_iterator(_head);
}3.3 默认成员函数
3.3.1构造函数

(1)创建哨兵位,缺省参数
List(T val=T())
:_head(new Node)
{
	_head->_last = _head;
	_head->_prev = _head;
	_head->_val = val;
}(2)迭代器区间构造
template <class InputIterator>
List(InputIterator first, InputIterator last)
{
	_head=new Node;
	_head->_last = _head;
	_head->_prev = _head;
	_head->_val = T();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
push_back是尾插,这个我们之后再写,先挖个坑
3.3.2 析构函数
~List()
{
	clear();
	delete _head;
	_head = nullptr;
}clear的作用是消除除了哨兵位之外的节点。咱们后面实现
3.3.3拷贝构造函数
摩登写法:
List(List<T>& l)
{
	_head(new Node);
	_head->_last = _head;
	_head->_prev = _head;
	_head->_val = T();
	List<T>tmp(l.begin(), l.end());
	swap(tmp);
}3.3.4 operator=
现代写法
注意事项:
- 传参变成传值,这样就会拷贝构造出一个临时对象
- 再使用list中的swap,交换*this和tmp的值,完成赋值重载
List<T>& operator=(List<T>l)
{
	swap(l);
	return*this;
}3.4 修改

3.4.1 insert
注意事项:
迭代器不会失效
void intsert(iterator pos,T val)//原本在这个位置的节点向后移
{
	Node* cur = pos._Node;
	Node* prev = cur->_prev;
	Node* NewNode = new Node(val);
	NewNode->_prev = prev;
	prev->_last = NewNode;
	NewNode->_last = cur;
	cur->_prev = NewNode;
}3.4.2 push_front
头插
void push_front(T val)
{
	Node* NewNode = new Node(val);
	Node* last = _head->_last;
	NewNode->_prev = _head;
	NewNode->_last = last;
	last->_prev = NewNode;
	_head->_last = NewNode;
}3.4.3 push_back
尾插
void  push_back(T val)
{
	Node* NewNode = new Node(val);
	Node* tail = _head->_prev;
	tail->_last = NewNode;
	NewNode->_prev = tail;
	NewNode->_last = _head;
	_head->_prev = NewNode;
}3.4.4 erase
指定位置删除
注意事项:
- assert断言,防止删除哨兵位
- 返回删除节点的下一位,防止迭代器失效
iterator erase(iterator pos)
{
assert(pos!=end());
	Node* cur = pos._Node;
	Node* prev = cur->_prev;
	Node* last = cur->_last;
	prev->_last =last;
	last->_prev = prev;
	delete cur;
	return iterator(last);
}3.4.5 pop_front
void  pop_front()
{
	erase(begin());
}3.4.6 pop_back
void pop_back()
{
	erase(--end());
}3.4.7 clear
清除所有结点(除哨兵位以外)
void clear()
{
	Node* New = _head->_last;
	while (New != _head)
	{
		Node* News = New->_last;
		delete New;
		New = News;
	}
}3.4.8 swap
交换两个list类的值
注意事项:
使用std库中的swap函数,要带有std::,不然会被认成你所写的swap,进而导致无限递归。
void swap(List<T>&l)
{
	std::swap(_head,l._head);
}3.4.9list的迭代器失效
4. list与vector的对比
 
 
  我的模拟源码仅供参考,第一次写,有错误欢迎在评论区指出
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
struct ListNode//驼峰式命名
{
	ListNode<T>* _prev;
	ListNode<T>* _last;
	T _val;
	ListNode(const T &val=T())//缺省参数
		:_prev(nullptr)
	    ,_last(nullptr)
		,_val(val)
	{}
};
template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;//为了方便使用节点类,我们给它重命名一下
	typedef ListIterator<T, Ref,  Ptr>self;//原理同上
	ListIterator(Node* ptr)
		:_Node(ptr)
	{}
	Ref operator*()
	{
		return _Node->_val;
	}
	Ptr operator->()
	{
		return &_Node->val;
	 }
	self& operator++()
	{
		_Node = _Node->_last;
		return _Node;
	}
	self operator++(int)
	{
		Node* tmp = _Node;
		_Node = _Node->last;
		return tmp;
	}
	self operator--(int)
	{
		Node* tmp = _Node;
		_Node = _Node->_prev;
		return tmp;
	}
	self operator--()
	{
		_Node = _Node->_prev;
		return _Node;
	}
	bool operator==(self& s)
	{
		return _Node == s._Node;
	}
	bool operator!=(self& s)
	{
		return _Node != s._Node;
	}
	Node* _Node;
};
template<class T>
class List
{
public:
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&,const T*> const_iterator;
	typedef ListNode<T> Node;
	iterator begin()
	{
		return iterator(_head->_last);
	}
	const_iterator begin()const
	{
		return const_iterator(_head->_last);
	}
	iterator end()
	{
		return iterator(_head);
	}
	const_iterator end()const
	{
		return const_iterator(_head);
	}
	List(T val=T())
	:_head(new Node)
	{
		_head->_last = _head;
		_head->_prev = _head;
		_head->_val = val;
	}
	template <class InputIterator>
	List(InputIterator first, InputIterator last)
	{
		_head=new Node;
		_head->_last = _head;
		_head->_prev = _head;
		_head->_val = T();
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}
	~List()
	{
		clear();
		delete _head;
		_head = nullptr;
	}
	List(List<T>& l)
	{
		_head(new Node);
		_head->_last = _head;
		_head->_prev = _head;
		_head->_val = T();
		List<T>tmp(l.begin(), l.end());
		swap(tmp);
	}
	List<T>& operator=(List<T>l)
	{
		swap(l);
		return*this;
	}
	void intsert(iterator pos,T val)//原本在这个位置的节点向后移
	{
		Node* cur = pos._Node;
		Node* prev = cur->_prev;
		Node* NewNode = new Node(val);
		NewNode->_prev = prev;
		prev->_last = NewNode;
		NewNode->_last = cur;
		cur->_prev = NewNode;
	}
	void push_front(T val)
	{
		Node* NewNode = new Node(val);
		Node* last = _head->_last;
		NewNode->_prev = _head;
		NewNode->_last = last;
		last->_prev = NewNode;
		_head->_last = NewNode;
	}
	void  push_back(T val)
	{
		Node* NewNode = new Node(val);
		Node* tail = _head->_prev;
		tail->_last = NewNode;
		NewNode->_prev = tail;
		NewNode->_last = _head;
		_head->_prev = NewNode;
	}
	iterator erase(iterator pos)
	{
		assert(pos != end());
		Node* cur = pos._Node;
		Node* prev = cur->_prev;
		Node* last = cur->_last;
		prev->_last = last;
		last->_prev = prev;
		delete cur;
		return iterator(last);
	}
	void  pop_front()
	{
		erase(begin());
	}
	void pop_back()
	{
		erase(--end());
	}
	void clear()
	{
		Node* New = _head->_last;
		while (New != _head)
		{
			Node* News = New->_last;
			delete New;
			New = News;
		}
	}
	void swap(List<T>&l)
	{
		std::swap(_head,l._head);
	}
private:
	Node* _head;
};
总结
学习完list类,与之前相比,最大的收获就是迭代器的设计,同时也对多参数模板有了更深一步的了解,虽然过程艰辛,但是,List,over!
相信下一个难题也会被攻克
觉得有用的话,就点个赞支持一下吧😘😘😘




















