通过list模拟实现学习模版使用以及迭代器的封装

一.list介绍

在数据结构中,有一种链表结构是带哨兵位的头节点,其每一个节点都有一个指向上一个节点的指针以及指向下一个节点的指针,从而形成一种环状结构。

详细介绍list的模拟实现及特点_list

STL中list就采用了带头双向循环链表的结构,弥补了(vector)连续空间的结构不适于对中间数据频繁插入删除的缺点。

二.list框架

先对list进行分析,可以把它分成几个部分梳理思路,分别实现:

1.list成员变量:节点实现

2.list基本成员函数:构造析构等

3.迭代器

4.list功能实现:增删查改

三.模拟实现

有了大的框架,我们可以开始处理细节问题,完成list的基本功能实现。

1.节点类处理:

链表的一个节点本质上分为三部分:两个指针以及数据值详细介绍list的模拟实现及特点_迭代器封装_02

如果将这三者作为三个成员变量,当我们对一个节点进行操作(传参)时,需要同时传三个数据,这是非常不方便,所以考虑将其封装成类。

template<class T>//为了满足多种数据类型的需求,使用模板
	struct list_node
	{
		list_node<T>* _next;//指向下一个节点
		list_node<T>* _prev;//指向上一个节点
		T _val;//数据

		list_node(const T& val = T())//构造函数
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}
	};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

注意:对于T,不一定是内置类型,所以构造函数需要给缺省值调用类型本身的构造函数

2.结构

我们已经知道,list是带头的双向循环链表,

明确了成员变量,list的基本结构就已经出来了:

class list
{
 //typedef:
public:
	typedef list_node<T> Node;
  
 //成员函数:
public:
  //构造
  list()
	{
		_head = new Node;
		_head->_prev = _head;
		_head->_next = _head;
  }
  
  //成员变量:
private:
	Node* _head;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

详细介绍list的模拟实现及特点_list_03

3.迭代器的封装

迭代器(Iterator)是C++ 标准库中的一个重要概念,它提供了一种通用的方式来访问容器(如  std::vector 、 std::list 、 std::map  等)中的元素,同时隐藏了容器的内部实现细节。

个人认为,在list的实现里大部分成员函数都比较常规,只有迭代器的实现需要我们更多注意,其也是我们学习list时需要重点理解和掌握的。

在vector、string里,迭代器本质上是指针,通过对指针指向地址空间+-等行为得到数据。但对于list而言,是否可以直接将变量指针作为iterator?

//这样?
typedef T* iterator;
//或者是这样?
typedef Node* iterator;
  • 1.
  • 2.
  • 3.
  • 4.

但仔细一想就会发现,list的节点空间并不连续,如果这样定义iterator,当对其+-时指针显然成为了野指针。

详细介绍list的模拟实现及特点_STL_04

所以我们迭代器的操作需要特殊处理:对运算符重载;而为了完成这些函数操作,我们需要再一次把迭代器封装:

template<class T>
 //需要在外界(如:list类)中访问迭代器中的成员变量or函数
    //为了使用方便直接定义成了struct--public的
struct __list_iterator
{
    //为了方便表示,对类型typedef
	typedef list_node<T> Node;
	typedef __list_iterator<T> self;
	Node* _node;
    
    //构造函数,当使用iterator时,将指向的节点对__list_iterator类的成员初始化
	__list_iterator(Node* node)
		:_node(node)
	{}

	//操作符重载
	T& operator*()
	{
		return _node->_val;
	}
	
	self& operator++()
	{
		_node = _node->_next//找到下一个节点;
		return *this;//返回iterator
	}
	self& operator--()
	{
		_node = _node->_prev;
		return *this;//返回iterator
	}
	self operator++(int)//临时变量tmp出了作用域不存在,不能返回引用
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	bool operator!=(const self& it)const
	{
		return _node != it._node;
	}
	bool operator==(const self& it)const
	{
		return _node == it._node;
	}
	T* operator->()
	{
		return &_node->_val;
    //当数据类型是自定义类型时(struct A) 
        //严格来说应使用 it->->a1 ; it->->a2
	//it.operator->()  &A   (&A)->a1;
	//那为什么不设计成直接返回T?
	//那样也是一样 it.operator->() A 进而访问成员a1 A.a1 ----》总体还是两个it->.a1反而不好处理
	//编译器特殊处理,会省略一个-> it->a1即可
	}
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

注意:对于迭代器类不需要析构函数,因为当我们不再使用迭代器时,节点并不应该被销毁,节点空间应在list类中管理释放。

而拷贝构造同样不用我们显式去写,当用一个已存在的iterator去定义另一个iterator时,它们指向同一块空间(一个节点),那么只需要一个完成浅拷贝的拷贝构造--编译器自动生成的就可以使用。

//那么将 __list_iterator<T>作为迭代器,在list类中对其
typedef list_node<T> Node;
typedef __list_iterator<T> iterator;
  • 1.
  • 2.
  • 3.

有了普通迭代器,const的迭代器又该如何实现?

当使用const_iterator,不可修改节点内数据,也就是说,只要保证__list_iterator类中的函数返回值都是const类型,我们就无法对来自iterator操作中得到数据进行修改。

而为了满足const需求,再写一个类去实现几乎相同的工作(仅仅是返回类型不同)是很浪费的。

这时,模版又是一个很好的工具。

__list_iterator中,发现返回值分成四种:T&,self&,T*,bool self是指向另一节点指针与数据无关,那么我们需要控制的只是T& ,和 T*。

所以,增加两个模板参数:Ref,Ptr分别代表T&,和T*

template<class T,class Ref,class Ptr>
  • 1.

那么当需要const类型迭代器时:只需要给模板参数为:const T ,const T&,const T* 来实例化。

//假设T为int ,当使用iterator时,会对应给T:int;Ref:int& ;Ptr:int*
typedef __list_iterator<T,T&,T*> iterator;
//而使用const_iterator时,给T:int;Ref:const int& ;Ptr:const int*
										//确保了成员函数的返回值是const
typedef __list_iterator<T,const T&,const T*> const_iterator;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
4.list尾插:
//以尾插举例:
void push_back(const T& x)
	{
		//尾插即在哨兵位的前面插入
		Node* tail = _head->_prev;//通过头节点找尾
		Node* newnode = new Node(x);//调默认构造-创建新节点

		tail->_next = newnode;
		newnode->_prev = tail;
		newnode->_next = _head;
		_head->_prev = newnode;
  /*尾节点的下一个连新节点
    新节点的前一个是尾节点
    同理将新节点和头节点相连
    形成一个环*/
	}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

其他函数实现过程基本相同,在实现时要保证链表的完整性,以及迭代器失效的问题(删除节点后让iterator指向下一个节点)。

5.完整代码

头文件:#include<iostream>,#include<assert.h>

节点类:
template<class T>
struct list_node
{
	list_node<T>* _next;
	list_node<T>* _prev;
	T _val;

	list_node(const T& val = T())
		:_next(nullptr)
		, _prev(nullptr)
		, _val(val)
	{}
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
迭代器:
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)
	{}

	//操作符重载
	Ref operator*()
	{
		return _node->_val;
	}
	self& operator++()
	{
		_node = _node->_next;
		return *this;//返回iterator
	}
	self& operator--()
	{
		_node = _node->_prev;
		return *this;//返回iterator
  }
	self operator++(int)
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	bool operator!=(const self& it)const
	{
		return _node != it._node;
	}
	bool operator==(const self& it)const
	{
		return _node == it._node;
	}
	Ptr operator->()
	{
		return &_node->_val;
  }
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
list类:
template<class T>
class list
{
public:
	typedef list_node<T> Node;
	typedef __list_iterator<T,T&,T*> iterator;
	typedef __list_iterator<T,const T&,const T*> const_iterator;
public:
//iterator
	iterator begin()
	{
		//return _head->_next;//隐式类型转换(单参数的构造函数支持隐式类型缓缓)
		return iterator(_head->_next);//生成一个匿名对象 -->操作中会给it
	}
	const_iterator begin()const
	{
		return _head->_next;
	}
	iterator end()
	{
		return _head;//传值返回,临时对象-常性
	}
	const_iterator end()const
	{
		return _head;
	}


	void empty_init()
	{
		_head = new Node;
		_head->_prev = _head;
		_head->_next = _head;
	}
	
	list()
	{
		empty_init();
		/*_head = new Node;
		_head->_prev = _head;
		_head->_next = _head;*/
	}
	list(const list<T>& lt)
	{
		empty_init();

		for (auto& e : lt)
		{
			push_back(e);
		}
	}
	~list()
	{
		//...
		clear();

		delete _head;
		_head = nullptr;
	}
  
  //清除所有的数据(空间)--不清理哨兵位的头结点
	void clear()
	{
		iterator it = begin();
		while (it != end())
		{
			it = erase(it);
		}
	}
	void push_front(const T& x)
	{
		insert(begin(), x);
	}
	void pop_back()
	{
		erase(--end());
	}
	void push_back(const T& x)
	{
		//尾插即在哨兵位的前面插入
		insert(end(), x);
		//Node* tail = _head->_prev;
		//Node* newnode = new Node(x);//默认构造

		//tail->_next = newnode;
		//newnode->_prev = tail;
		//newnode->_next = _head;
		//_head->_prev = newnode;
	}
	void pop_front()
	{
		erase(begin());
	}
  //在pos位置前插入
	iterator insert(iterator pos, const T& x)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* newnode = new Node(x);
		prev->_next = newnode;
		newnode->_prev = prev;

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

		return newnode;
	}
	iterator erase(iterator pos)
	{
		assert(pos != end());

		Node* cur = pos._node;
		Node* next = cur->_next;
		Node* prev = cur->_prev;

		next->_prev = prev;
		prev->_next = next;

		delete cur;
    //迭代器失效问题,pos位置已经被删除--》要返回下一个位置的迭代器

		return next;
	}
	size_t size()
	{
		size_t sz = 0;
		iterator it = begin();
		while (it != end())
		{
			++sz;
			++it;
		}
		return sz;//也可以增加一个成员变量的方法记录size
	}
private:
	Node* _head;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.