【C++初阶】八、STL---list模拟实现

120 阅读19分钟

一、模拟实现接口总览

实现接口总览

//无参构造
		list()
//迭代器区间构造
		template <class InputIterator>
//拷贝构造 - 现代写法
		list(const list<T>& lt)
//赋值重载 - 现代写法
		list<T>& operator=(list<T> lt)
//析构函数
		~list()

//Iterators
		iterator end()
        iterator begin()
		const_iterator end()const
		const_iterator begin()const

//Capacity
		bool empty()const
		size_t size() const

//Modifiers
		iterator insert(iterator pos, const T& x)
		iterator erase(iterator pos)
		void push_back(const T& x)
		void pop_back()
		void push_front(const T& x)
		void pop_front()
		void swap(list<T>& lt)
		void clear()

注:list 的模拟实现,重点放在 list 模拟实现的整体框架和迭代器的实现上,实现参考版本:SGI版 STL3.0 的写法

        SLT 的模拟实现,不是为了造更好的轮子,而是为了去学习它,理解它的底层,自己造一次,心里会更清楚,更利于加深对它们的理解 

二、整体框架搭建

注:模拟实现的代码要写在自己的命名空间里面,否则会与库中的产生冲突

2.1 节点类框架搭建 

        首先,我们知道 list 就是一个带头双向循环链表

        因此,我们若要实现 list,则首先需要实现一个结点类。而一个结点需要存储的信息有:数据、前一个结点的地址、后一个结点的地址,于是该结点类的成员变量也就出来了(数据、前驱指针、后继指针)-> (_data、_prve、_next) ,还有一个哨兵位的头节点 _head

框架: 

#pragma once

#include <iostream>
using namespace std;

namespace fy
{
	template<class T>
	struct list_node
	{

		//成员变量
		list_node<T>* _prve; //前驱指针
		list_node<T>* _next; //后继指针
		T _data;			 //数据域
	};
}

该类只需要对节点进行初始化即可,即该类只需提供构造函数

#pragma once

#include <iostream>
using namespace std;

namespace fy
{
	template<class T>
	struct list_node
	{
		//成员函数
		//构造函数
		list_node(const T& x)
			:_prve(nullptr)
			, _next(nullptr)
			, _data(x)
		{}

		//成员变量
		list_node<T>* _prve; //前驱指针
		list_node<T>* _next; //后继指针
		T _data;			 //数据域
	};
}

 2.2 头节点类框架搭建(list模拟实现主体)

        list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可,list 模拟实现主体也在这个类里面

节点类为什么不封装在头结点类里面?节点类为什么使用 struct 不使用 class?

        C++ 不喜欢使用内部类;使用 struct 和 class 都可以,看你喜欢使用哪一个,struct 没有访问超限定符限制,如果使用 class 需要把成员变量设置为公有

#pragma once

#include <iostream>
using namespace std;

namespace fy
{
    //结点类
	template<class T>
	struct list_node
	{
		//成员函数
		//构造函数
		list_node(const T& x)
			:_prve(nullptr)
			, _next(nullptr)
			, _data(x)
		{}

		//成员变量
		list_node<T>* _prve; //前驱指针
		list_node<T>* _next; //后继指针
		T _data;			 //数据域
	};

	//头结点类(list模拟实现主体)
	template<class T>
	class list
	{
		typedef list_node<T> node;

	public:


	private:
		node* _head; //头结点
	};
}

主体实现下面详解,这里先说一下框架 

2.3 迭代器类框架搭建

2.3.1 迭代器分类

        前面学习了 string 和 vector,我们已经知道迭代器的行为像指针,迭代器是内嵌类型(封装在类里面或内部类里面)

迭代器分类:

  1. 单向迭代器:只能 ++,不能 --;比如单链表
  2. 双向迭代器:既能 ++ 也能 --; 比如 list
  3. 随机迭代器:即可以随机访问,既能 ++ 也能 --,还能 + 还能 -;比如:vector、string

2.3.2 list 所需迭代器分析

        list 模拟实现最重点是 list迭代器的实现,因为这里的迭代器的实现和我们之前讲的实现方式都不同。我们之前讲的 string 和 vector 的迭代器都是一个原生指针,实现起来是非常简单的

为什么原生指针就可以满足 string 和 vector 迭代器的需求?

        因为 string 和 vector 开辟的空间都是一块连续的内存空间,通过指针进行自增(++)、自减(--)以及解引用(*)等操作,就可以对相应位置的数据进行一系列操作,因此原生指针可以满足 string 和 vector 迭代器的需求

        但是 list 是一个链表,在空间上不是连续的,原生指针如何往后走? 原生指针无法进行 list 迭代器的各项操作(++、--、* 等等),这就说明原生指针已经不满足 list 迭代器的需要了

        而迭代器的意义就是,让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行访问,可分为两点:

  1. 封装底层实现,不暴露底层实现的细节
  2. 提供统一的访问方式,降低使用成本

        所以,想要满足 list 迭代器的需求,就必须对指针进行封装和重载,让其支持 list 迭代器的需求 

如何进行封装和重载?单独提供一个类,在里面就那些封装和重载

2.3.3 list普通迭代器实现

框架: 

//迭代器类
template<class T>
struct __list_iterator
{
	typedef list_node<T> node;

	//成员变量
	node* _pnode;
};

(1)构造函数 

        迭代器类实际上就是对结点指针进行了封装,其成员变量就只有一个,那就是结点指针,其构造函数直接根据所给结点指针构造一个迭代器对象即可,即迭代器类只需要提供构造函数

//构造函数
__list_iterator(node* p)
	:_pnode(p)
{}

(2)++运算符的重载

前置++:让结点指针指向后一个结点,然后再返回 ++ 后的结点指针即可

//前置++
__list_iterator<T>& operator++()
{
	_pnode = _pnode->_next; //让结点指针指向下一个结点
	return *this; //返回++后的结点指针
}

后置++:先记录当前结点指针,然后让结点指针指向后一个结点,然后再返回 ++ 前的结点指针即可

//后置++
__list_iterator<T>& operator++(int)
{
	__list_iterator<T> tmp(*this); //记录当前结点指针
	_pnode = _pnode->_next; //让结点指针指向下一个结点
	return tmp; //返回++前的结点指针
}

(3)--运算符的重载

前置--:让结点指针指向前一个结点,然后再返回 -- 后的结点指针即可

//前置--
__list_iterator<T>& operator--()
{
	_pnode = _pnode->_prve; //让结点指针指向前一个结点
	return *this; //返回--后的结点指针
}

后置--:先记录当前结点指针,然后让结点指针指向前一个结点,然后再返回 -- 前的结点指针即可

//后置--
__list_iterator<T>& operator--(int)
{
	__list_iterator<T> tmp(*this); //记录当前结点指针
	_pnode = _pnode->_prve; //让结点指针指向前一个结点
	return tmp; //返回--前的结点指针
}

(4)解引用就是取结点 _node 里的数据 _data

T& operator*()
{
	return _pnode->_data;
}

(5)!=运算符的重载

两个迭代器结点的指针指向的是同一个结点,那就说明是相等的迭代器,否则不相等

bool operator!=(const __list_iterator<T>& it)
{
	return _pnode != it._pnode;
}

就先介绍到这,其他就先不介绍了

迭代器代码总览

//迭代器类
template<class T>
struct __list_iterator
{
	typedef list_node<T> node;

	//成员函数
	//构造函数
	__list_iterator(node* p)
		:_pnode(p)
	{}

	T& operator*()
	{
		return _pnode->_data;
	}

	//前置++
	__list_iterator<T>& operator++()
	{
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return *this; //返回++后的结点指针
	}

	//后置++
	__list_iterator<T>& operator++(int)
	{
		__list_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return tmp; //返回++前的结点指针
	}

	//前置--
	__list_iterator<T>& operator--()
	{
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return *this; //返回--后的结点指针
	}

	//后置--
	__list_iterator<T>& operator--(int)
	{
		__list_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return tmp; //返回--前的结点指针
	}


	bool operator!=(const __list_iterator<T>& it)
	{
		return _pnode != it._pnode;
	}

	bool operator==(const __list_iterator<T>& it)
	{
		return _pnode == it._pnode;
	}

	//成员变量
	node* _pnode;
};

2.3.4 list的const迭代器实现

        普通迭代器是可读可写,const 迭代器是只读,不支持修改数据,即普通迭代器访问普通对象,可读可写;const 迭代器访问 const 对象,可读但不可写

直接在类成员函数前面加 const ,这种写法是错误的

T& operator*()
const T& operator*()const//error

第一个:非 const 对象可以调用(即可以 *),也可以进行 ++ 等操作,没毛病

第二个:const 对象调用(可以 *),但是不能进行 ++ 等操作,因为 ++ 没有 const 版本,++ 本来就是写函数,所以这种方法不行

        const 迭代器实现的方法是把  list_iterator 这个类复制一下,然后把名称改成  __const_list_iterator

//迭代器类
template<class T>
struct __list_iterator
{
	typedef list_node<T> node;

	//成员函数
	//构造函数
	__list_iterator(node* p)
		:_pnode(p)
	{}

	T& operator*()
	{
		return _pnode->_data;
	}

	//前置++
	__list_iterator<T>& operator++()
	{
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return *this; //返回++后的结点指针
	}

	//后置++
	__list_iterator<T>& operator++(int)
	{
		__list_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return tmp; //返回++前的结点指针
	}

	//前置--
	__list_iterator<T>& operator--()
	{
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return *this; //返回--后的结点指针
	}

	//后置--
	__list_iterator<T>& operator--(int)
	{
		__list_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return tmp; //返回--前的结点指针
	}


	bool operator!=(const __list_iterator<T>& it)
	{
		return _pnode != it._pnode;
	}

	bool operator==(const __list_iterator<T>& it)
	{
		return _pnode == it._pnode;
	}

	//成员变量
	node* _pnode;
};

//const迭代器类
template<class T>
struct __list_const_iterator
{
	typedef list_node<T> node;

	//成员函数
	//构造函数
	__list_const_iterator(node* p)
		:_pnode(p)
	{}

	const T& operator*()
	{
		return _pnode->_data;
	}

	//前置++
	__list_const_iterator<T>& operator++()
	{
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return *this; //返回++后的结点指针
	}

	//后置++
	__list_const_iterator<T>& operator++(int)
	{
		__list_const_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return tmp; //返回++前的结点指针
	}

	//前置--
	__list_const_iterator<T>& operator--()
	{
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return *this; //返回--后的结点指针
	}

	//后置--
	__list_const_iterator<T>& operator--(int)
	{
		__list_const_iterator<T> tmp(*this); //记录当前结点指针
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return tmp; //返回--前的结点指针
	}


	bool operator!=(const __list_const_iterator<T>& it)
	{
		return _pnode != it._pnode;
	}

	bool operator==(const __list_const_iterator<T>& it)
	{
		return _pnode == it._pnode;
	}

	//成员变量
	node* _pnode;
};

         然后 list 的const的迭代器就完成了,这种实现方式可以是可以,但是这么实现好像有点搓啊!代码极度冗余!!这个 const 迭代器和普通迭代器也就是类型名称和返回值不一样而已

        我们去看看源码,看看大佬是怎么写的(源码已经上传资源了,审核中)

        大佬在定义 template 模板的时增加一个模板参数 Ref(第三个模板参数下面讲),然后只需修改一下就可以了,const 迭代器和 非 const 迭代器就完成了

但是改起来太麻烦了,直接进行 typedef 一下

typedrf __list_iterator<T, Ref> Self;
//迭代器类
// 同一个类模板实例化出的两个类型
// typedef __list_iterator<T, T&> iterator;
// typedef __list_iterator<T, const T&> const_iterator;
template<class T, class Ref>
struct __list_iterator
{
	typedef list_node<T> node;
	typedrf __list_iterator<T, Ref> Self;

	//成员函数
	//构造函数
	__list_iterator(node* p)
		:_pnode(p)
	{}

	Ref operator*()
	{
		return _pnode->_data;
	}
	//前置++
	Self& operator++()
	{
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return *this; //返回++后的结点指针
	}
	//后置++
	Self& operator++(int)
	{
		Self tmp(*this); //记录当前结点指针
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return tmp; //返回++前的结点指针
	}
	//前置--
	Self& operator--()
	{
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return *this; //返回--后的结点指针
	}
	//后置--
	Self& operator--(int)
	{
		Self tmp(*this); //记录当前结点指针
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return tmp; //返回--前的结点指针
	}

	bool operator!=(const Self& it)
	{
		return _pnode != it._pnode;
	}

	bool operator==(const Self& it)
	{
		return _pnode == it._pnode;
	}

	//成员变量
	node* _pnode;
};

 这样const版本的迭代器和非const版本的迭代器就完成了,接下来说迭代器的第三个模板参数 Ptr

2.3.5 list迭代器第三个模板参数Ptr

->运算符的重载

T* operator->()
{
	return &_pnode->_data;
}

这里也是 const 和非const 版本的问题,直接加第三个模板参数 Ptr 即可解决

参数修改直接在这两处添加即可,这就是 typedef 好处

//迭代器类
// 同一个类模板实例化出的两个类型
// 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;

	//成员函数
	//构造函数
	__list_iterator(node* p)
		:_pnode(p)
	{}

	Ptr operator->()
	{
		return &_pnode->_data;
	}

	Ref operator*()
	{
		return _pnode->_data;
	}
	//前置++
	Self& operator++()
	{
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return *this; //返回++后的结点指针
	}
	//后置++
	Self& operator++(int)
	{
		Self tmp(*this); //记录当前结点指针
		_pnode = _pnode->_next; //让结点指针指向下一个结点
		return tmp; //返回++前的结点指针
	}
	//前置--
	Self& operator--()
	{
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return *this; //返回--后的结点指针
	}
	//后置--
	Self& operator--(int)
	{
		Self tmp(*this); //记录当前结点指针
		_pnode = _pnode->_prve; //让结点指针指向前一个结点
		return tmp; //返回--前的结点指针
	}

	bool operator!=(const Self& it)
	{
		return _pnode != it._pnode;
	}

	bool operator==(const Self& it)
	{
		return _pnode == it._pnode;
	}

	//成员变量
	node* _pnode;
};

        某些情景下,我们使用迭代器的时候可能会用到 -> 运算符,后面在讲

迭代器类的拷贝构造、赋值重载、析构函数为什么不用实现?

        拷贝构造和赋值不需要自己实现,默认生成的即可,当前迭代器赋值给另一个迭代器是不需要深拷贝的,浅拷贝就可以,拷贝构造也是;析构释放空间的事情不归迭代器类管理,迭代器类只需构建迭代器对象即可,释放空间归 list 管理

到这里,基本框架已经打好了,下面可以直接实现 list 了

2.4 整体框架代码

#pragma once

#include <iostream>
using namespace std;

namespace fy
{
	//结点类
	template<class T>
	struct list_node
	{
		//成员函数
		//构造函数
		list_node(const T& x)
			:_prve(nullptr)
			, _next(nullptr)
			, _data(x)
		{}

		//成员变量
		list_node<T>* _prve; //前驱指针
		list_node<T>* _next; //后继指针
		T _data;			 //数据域
	};

	//迭代器类
	// 同一个类模板实例化出的两个类型
	// 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;

		//成员函数
		//构造函数
		__list_iterator(node* p)
			:_pnode(p)
		{}

		Ptr operator->()
		{
			return &_pnode->_data;
		}

		Ref operator*()
		{
			return _pnode->_data;
		}
		//前置++
		Self& operator++()
		{
			_pnode = _pnode->_next; //让结点指针指向下一个结点
			return *this; //返回++后的结点指针
		}
		//后置++
		Self& operator++(int)
		{
			Self tmp(*this); //记录当前结点指针
			_pnode = _pnode->_next; //让结点指针指向下一个结点
			return tmp; //返回++前的结点指针
		}
		//前置--
		Self& operator--()
		{
			_pnode = _pnode->_prve; //让结点指针指向前一个结点
			return *this; //返回--后的结点指针
		}
		//后置--
		Self& operator--(int)
		{
			Self tmp(*this); //记录当前结点指针
			_pnode = _pnode->_prve; //让结点指针指向前一个结点
			return tmp; //返回--前的结点指针
		}

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}

		bool operator==(const Self& it)
		{
			return _pnode == it._pnode;
		}

		//成员变量
		node* _pnode;
	};

	//头结点类(list模拟实现主体)
	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;
		
	private:
		node* _head; //头结点
	};
}

list 模拟实现,最重要的就是上面框架的实现了,下面开始实现 list 的函数接口

三、list函数接口模拟实现

3.1 构造函数

3.1.1 无参构造

        list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可,直接写一个empty_initialize() 函数,进行复用,注:为了方便,_size 是新添加的成员变量,_size 也要置 0

list()
{
	empty_initialize();
}

void empty_initialize()
{
	_head = new node(T());
	_head->_next = _head;
	_head->_prve = _head;
	_size = 0;
}

3.1.2 迭代器区间构造

        提供一段迭代器区间进程构造,创建新头结点,遍历尾插到新头结点即可

//迭代器区间构造
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
	//初始化头结点
	empty_initialize();
	//遍历进行尾插
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

3.1.3 拷贝构造

        拷贝构造要注意深浅拷贝的问题 

(1)传统写法

        也是创建新头结点,遍历尾插到新头结点即可

//拷贝构造
//传统写法
list(const list<T>& lt)
{
	empty_initialize();//初始化头结点
	for (const auto& e : lt)//遍历进行尾插
	{
		push_back(e);
	}
}

(2)现代写法

        复用迭代器区间构造,创建一个 tmp,再进行交换即可

//现代写法
list(const list<T>& lt)
{
	empty_initialize();//初始化头结点
	list<T> tmp(lt.begin(), lt.end());
	swap(tmp);//两个对象交换
}

3.2 赋值重载

        赋值重载也要注意深浅拷贝的问题

(1)传统写法

        先调用clear函数将原容器清空,然后将容器 lt 当中的数据,通过遍历的方式一个个尾插到清空后的容器当中即可

//赋值重载
list<T>& operator=(const list<T>& lt)
{
	if (this != &lt)
	{
		clear();//清空容器
		for (const auto& e : lt)//遍历进行尾插
		{
			push_back(e);
		}
	}
	return *this;
}

(2)现代写法

        传参使用传值传参,自动调用其拷贝构造函数,然后交换两个对象即可

//现代写法
list<T>& operator=(list<T> lt)//传值传参,自动调用拷贝构造
{
	swap(lt);//交换两个对象
	return *this;
}

3.3 析构函数

        在 clear 函数实现的情况下,可以直接复用,最后再将头结点删除释放置空即可

//析构函数
~list()
{
	clear();//复用
	delete _head;//删除释放头结点
	_head = nullptr;//置空
}

3.4 Iterators

首先对迭代器类 typedef 

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

(1) end

        end函数返回的是最后一个有效数据的下一个位置的迭代器,最后一个有效数据的下一个节点就是头结点,可以直接使用匿名对象

iterator end()
{
	//iterator it(_head);
	//return it;

	//使用匿名对象,便捷
	return iterator(_head);
}

const版本,只读

const_iterator end()
{
	return const_iterator(_head);
}

(2)begin 

        begin函数返回的是第一个有效数据的迭代器,第一个有效数据就是头结点的下一个结点

iterator begin()
{
	return iterator(_head->_next);
}

const版本,只读

const_iterator begin()
{
	return const_iterator(_head->_next);
}

3.5 Capacity

(1)empty

        empty用于判断容器 list 是否为空,直接判断该容器的 begin 和 end 所返回的迭代器,是否是同一个位置的迭代器即可,是同一个说明容器当中只有一个头结点 ;或者直接判断 _size 是否为 0,注:为了方便,_size 是新添加的成员变量

bool empty()const
{
	return _size == 0;
}

(2)size

        size函数用于获取当前容器当中的有效数据个数

size_t size() const
{
	return _size;
}

3.6 Modifiers

        这里很多函数都可以进行复用,首先实现 insert 和 erase 这两个函数,然后进行复用

(1)insert

        insert 可以在所给迭代器之前插入一个新结点,insert 之后,pos 迭代器不失效,我们也可以返回 新节点位置的迭代器

iterator insert(iterator pos, const T& x)
{
	//建立一个新节点
	node* newnode = new node(x);
	node* cur = pos._pnode;
	node* prve = cur->_prve;
	//与新节点 newnode 建立连续
	prve->_next = newnode;
	newnode->_prve = prve;
	newnode->_next = cur;
	cur->_prve = newnode;
	//有效数据+1
	++_size;
	return iterator(newnode);//返回新节点迭代器的位置
}

(2) erase

        erase函数可以删除所给迭代器位置的结点,删除之后 pos 位置的迭代器就失效了,为了防止失效,我们可以返回被删除节点的下一个节点的迭代器

iterator erase(iterator pos)
{
	assert(pos != end());//不能删头结点
	//节点重新建立联系
	node* prve = pos._pnode->_prve;
	node* next = pos._pnode->_next;
	prve->_next = next;
	next->_prve = prve;
	//释放被删除节点的空间,有效数据个数-1
	delete pos._pnode;
	--_size;

	return iterator(next);//返回被删除节点的下一个节点的迭代器
}

(3)push_back和pop_back

        push_back是尾插,即在头结点前插入结点;pop_back是尾删,即删除头结点的前一个结点。在已经实现了insert和erase函数的情况下,我们可以通过复用函数来实现push_back和pop_back函数

void push_back(const T& x)
{
	insert(end(), x);
}

void pop_back()
{
	erase(--end());
}

(4)push_front和pop_front

        push_front 是头插,即在第一个有效结点前插入结点,头结点后插入新节点;pop_front是头删,即删除第一个有效结点,头结点的下一个节点;也是直接复用 insert 和 erase 来实现

void push_front(const T& x)
{
	insert(begin(), x);
}

void pop_front()
{
	erase(begin());
}

(5)swap

        swap函数用于交换两个容器,只需将两个容器当中的头指针交换即可,还有 _size 交换一下即可

void swap(list<T>& lt)
{
	std::swap(_head, lt._head);//使用库 swap
	std::swap(_size, lt._size);
}

(6)clear

        clear函数用于清空容器,通过遍历的方式,逐个删除结点,只保留头结点即可

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);//重新赋值,防止迭代器it 失效
	}
}

四、list模拟实现全部代码

list.h

#pragma once

#include <iostream>
#include <assert.h>
using namespace std;

namespace fy
{
	//结点类
	template<class T>
	struct list_node
	{
		//成员函数
		//构造函数
		list_node(const T& x)
			:_prve(nullptr)
			, _next(nullptr)
			, _data(x)
		{}

		//成员变量
		list_node<T>* _prve; //前驱指针
		list_node<T>* _next; //后继指针
		T _data;			 //数据域
	};

	//迭代器类
	// 同一个类模板实例化出的两个类型
	// 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;

		//成员函数
		//构造函数
		__list_iterator(node* p)
			:_pnode(p)
		{}

		Ptr operator->()
		{
			return &_pnode->_data;
		}

		Ref operator*()
		{
			return _pnode->_data;
		}
		//前置++
		Self& operator++()
		{
			_pnode = _pnode->_next; //让结点指针指向下一个结点
			return *this; //返回++后的结点指针
		}
		//后置++
		Self& operator++(int)
		{
			Self tmp(*this); //记录当前结点指针
			_pnode = _pnode->_next; //让结点指针指向下一个结点
			return tmp; //返回++前的结点指针
		}
		//前置--
		Self& operator--()
		{
			_pnode = _pnode->_prve; //让结点指针指向前一个结点
			return *this; //返回--后的结点指针
		}
		//后置--
		Self& operator--(int)
		{
			Self tmp(*this); //记录当前结点指针
			_pnode = _pnode->_prve; //让结点指针指向前一个结点
			return tmp; //返回--前的结点指针
		}

		bool operator!=(const Self& it)
		{
			return _pnode != it._pnode;
		}

		bool operator==(const Self& it)
		{
			return _pnode == it._pnode;
		}

		//成员变量
		node* _pnode;
	};

	const迭代器类
	//template<class T>
	//struct __list_const_iterator
	//{
	//	typedef list_node<T> node;

	//	//成员函数
	//	//构造函数
	//	__list_const_iterator(node* p)
	//		:_pnode(p)
	//	{}

	//	const T& operator*()
	//	{
	//		return _pnode->_data;
	//	}

	//	//前置++
	//	__list_const_iterator<T>& operator++()
	//	{
	//		_pnode = _pnode->_next; //让结点指针指向下一个结点
	//		return *this; //返回++后的结点指针
	//	}

	//	//后置++
	//	__list_const_iterator<T>& operator++(int)
	//	{
	//		__list_const_iterator<T> tmp(*this); //记录当前结点指针
	//		_pnode = _pnode->_next; //让结点指针指向下一个结点
	//		retrun tmp; //返回++前的结点指针
	//	}

	//	//前置--
	//	__list_const_iterator<T>& operator--()
	//	{
	//		_pnode = _pnode->_prve; //让结点指针指向前一个结点
	//		return *this; //返回--后的结点指针
	//	}

	//	//后置--
	//	__list_const_iterator<T>& operator--(int)
	//	{
	//		__list_const_iterator<T> tmp(*this); //记录当前结点指针
	//		_pnode = _pnode->_prve; //让结点指针指向前一个结点
	//		retrun tmp; //返回--前的结点指针
	//	}


	//	bool operator!=(const __list_const_iterator<T>& it)
	//	{
	//		return _pnode != it._pnode;
	//	}

	//	bool operator==(const __list_const_iterator<T>& it)
	//	{
	//		return _pnode == it._pnode;
	//	}

	//	//成员变量
	//	node* _pnode;
	//};

	//头结点类(list模拟实现主体)
	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;

		//无参构造
		list()
		{
			empty_initialize();
		}

		void empty_initialize()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prve = _head;
			_size = 0;
		}

		//迭代器区间构造
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			//初始化头结点
			empty_initialize();
			//遍历进行尾插
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//拷贝构造
		传统写法
		//list(const list<T>& lt)
		//{
		//	empty_initialize();//初始化头结点
		//	for (const auto& e : lt)//遍历进行尾插
		//	{
		//		push_back(e);
		//	}
		//}
		
		//现代写法
		list(const list<T>& lt)
		{
			empty_initialize();//初始化头结点
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);//两个对象交换
		}

		//赋值重载
		//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(lt);//交换两个对象
			return *this;
		}

		//析构函数
		~list()
		{
			clear();//复用
			delete _head;//删除释放头结点
			_head = nullptr;//置空
		}

		//--------------------------------------------------------
		//Iterators
		iterator end()
		{
			//iterator it(_head);
			//return it;

			//使用匿名对象,便捷
			return iterator(_head);
		}

		iterator begin()
		{
			return iterator(_head->_next);
		}

		const_iterator end()const
		{
			return const_iterator(_head);
		}

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

		//--------------------------------------------------------
		//Capacity
		bool empty()const
		{
			return _size == 0;
		}

		size_t size() const
		{
			return _size;
		}

		//--------------------------------------------------------
		//Modifiers
		iterator insert(iterator pos, const T& x)
		{
			//建立一个新节点
			node* newnode = new node(x);
			node* cur = pos._pnode;
			node* prve = cur->_prve;
			//与新节点 newnode 建立连续
			prve->_next = newnode;
			newnode->_prve = prve;
			newnode->_next = cur;
			cur->_prve = newnode;
			//有效数据+1
			++_size;
			return iterator(newnode);//返回新节点迭代器的位置
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());//不能删头结点
			//节点重新建立联系
			node* prve = pos._pnode->_prve;
			node* next = pos._pnode->_next;
			prve->_next = next;
			next->_prve = prve;
			//释放被删除节点的空间,有效数据个数-1
			delete pos._pnode;
			--_size;

			return iterator(next);//返回被删除节点的下一个节点的迭代器
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front()
		{
			erase(begin());
		}

		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);//使用库 swap
			std::swap(_size, lt._size);
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);//重新赋值,防止迭代器it 失效
			}
		}

	private:
		node* _head; //头结点
		size_t _size;//记录节点有多少个
	};
}

Test.cpp

#include "list.h"

void Test_list()
{
	fy::list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);
	fy::list<int>::iterator it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	fy::list<int> lt2(lt1);//拷贝构造
	for (auto e : lt2)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;

	fy::list<int> lt3(lt2.begin(), lt2.end());//迭代器区间构造
	for (auto e : lt3)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;

	fy::list<int> lt4;
	lt4 = lt3;//赋值重载
	for (auto e : lt4)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;

	cout << "l4size: " << lt4.size() << endl;
	//清空l4
	lt4.clear();
	cout << "清空后lt4size: " << lt4.size() << endl;

	fy::list<int> lt5(lt1);
	for (auto e : lt5)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;
	lt5.push_front(0);
	lt5.push_front(-1);
	lt5.push_front(-2);
	for (auto e : lt5)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;
	lt5.pop_back();
	lt5.pop_back();
	lt5.pop_front();
	for (auto e : lt5)//编译器自动转换成迭代器
		cout << e << " ";
	cout << endl;
}

int main()
{
	Test_list();
	return 0;
}

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新