【C++】深入剖析list

本期我们来深入list的实现原理:

目录

一、STL中的list

二、list的模拟实现

2.1 搭建list的框架

2.2 list迭代器的实现

2.2.1 普通迭代器的实现

2.2.2 const类型迭代器的实现 

2.2.3 迭代器中->运算符重载实现

2.3 其他功能函数的实现

2.3.1 insert

2.3.2 erase

2.3.3 clean

2.3.4 析构函数

2.3.5 迭代器区间构造函数

2.3.6 拷贝构造函数

2.3.7 operator=

三、模拟实现list完整代码


一、STL中的list

我们先来看看在STL中是怎么定义list中的节点的:

template 
struct __list_node {
  typedef void* void_pointer;
  void_pointer next;
  void_pointer prev;
  T data;
};

我们可以看到定义该节点时用的是struct关键字而不是class,这是因为在STL标准库想要开放节点中的指针供编译人员访问。

下面是对于list这个类的定义:

template 
class list {
protected:
  typedef void* void_pointer;
  typedef __list_node list_node;
  typedef simple_alloc list_node_allocator;
public:      
  typedef T value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef list_node* link_type;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

public:
  typedef __list_iterator             iterator;
  typedef __list_iterator const_iterator;
}

剩下的源码我们就不过多解释,下面开始模拟实现:

二、list的模拟实现

2.1 搭建list的框架

我们先来简单写一写list的简单主要功能,搭建一个框架:

namespace lhs
{
	template
	struct _list_node
	{
		_list_node* _prev;
		_list_node* _next;
		T _data;

		//构造函数
		_list_node(const T& val = T())
			:_prev(nullptr),
			_next(nullptr),
			_data(val)
		{}
	};

	template
	class list
	{
	public:
		typedef _list_node node;
		//构造函数
		list()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		//尾插
		void push_back(const T& val)
		{
			node* newnode = new node(val);
			newnode->_next = _head;
			newnode->_prev = _head->_prev;
			_head->_prev->_next = newnode;
			_head->_prev = newnode;
		}
		//头插
		void push_front(const T& val)
		{
			node* newnode = new node(val);
			newnode->_next = _head->_next;
			newnode->_prev = _head;
			_head->_next->_prev = newnode;
			_head->_next = newnode;
		}
	private:
		node* _head;
	};
}

上面对于可以熟练运用带头双向循环链表的我们来说就是小菜一碟~

2.2 list迭代器的实现

2.2.1 普通迭代器的实现

我们在string和vector中实现迭代器十分的容易,因为它们的存储空间物理上都是连续的,我们可以对重命名的迭代器++即可。但是在list中就不可能了,因为基于链表实现的list存储空间并非是连续的。所以想要实现list中的迭代器要自定义另一个类型进行运算符重载:

template
struct __list_iterator
{
	typedef _list_node node;
	typedef __list_iterator self;
	node* _node;

	//构造函数
	__list_iterator(node* t)
		:_node(t)
	{}

	//运算符重载
	T& operator*()
	{
		return _node->_data;
	}
	self& operator++()//前置++
	{
		_node = _node->_next;
		return *this;
	}
	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;
	}
	self& operator-(size_t n)
	{
		for (size_t i = 0; i < n; ++i)
		{
			_node = _node->_prev;
		}
		return *this;
	}
	self& operator+(size_t n)
	{
		for (size_t i = 0; i < n; ++i)
		{
			_node = _node->_next;
		}
		return *this;
	}

	bool operator!=(self n)
	{
		return _node != n._node;
	}
	bool operator==(self n)
	{
		return _node == n._node;
	}
};

有了这个自定义类,我们直接将list中的节点传入该类型让其进行运算符重载即可:

template
class list
{
public:
	typedef _list_node node;
	//构造函数
	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}
	//迭代器
	typedef __list_iterator iterator;

	iterator begin()
	{
		return iterator(_head->_next);
	}
	iterator end()
	{
		return iterator(_head);
	}
private:
	node* _head;
};

2.2.2 const类型迭代器的实现 

但是对于const成员呢?我们可以像下面代码一样构建一个const版本的迭代器:【C++】深入剖析list_第1张图片

但是这个const迭代器和普通迭代器的唯一区别只在于重载*运行时的返回值不同,如果这样子写会造成代码的冗余

下面我们可以在自定义实现的迭代器里多加入一个模版参数Ref,来判断list调用迭代器时传入的是否是const类型的节点:

template//增加一个Ref模版参数来判断调用的是什么类型的迭代器
struct __list_iterator
{
	typedef _list_node node;
	typedef __list_iterator self;
	node* _node;

	//构造函数
	__list_iterator(node* t)
		:_node(t)
	{}

	//运算符重载
	Ref operator*()//通过第二个模版参数确定是不是const类型的返回值
	{
		return _node->_data;
	}
};

template
class list
{
public:
	typedef _list_node node;
	//构造函数
	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}

	//迭代器
	typedef __list_iterator iterator;//第二个模版参数传入T&调用普通迭代器
	typedef __list_iterator const_iterator;//第二个模版参数传入const T&调用const迭代器

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

private:
	node* _head;
};

2.2.3 迭代器中->运算符重载实现

如果list中存的是自定义类型的数据我们难免会用到->来访问具体数据

例如:

struct A
{
	int _a1;
	int _a2;
};
void test2()
{
	list l;
	A a = { 7,8 };
	l.push_back(a);
	l.push_back(a);
	l.push_back(a);
	list::iterator it = l.begin();
	while (it != l.end())
	{
		std::cout << (*it)._a1 << "," << (*it)._a2 << std::endl;
		++it;
	}
	std::cout << std::endl;
}

在上述代码中,我们想要对list中的A类型自定义变量具体成员进行访问,需要对迭代器*操作之后再加上.

这样十分的奇怪,不方便我们具体操作,所以下面我们来重载->操作符:

template
struct __list_iterator
{
	typedef _list_node node;
	typedef __list_iterator self;
	node* _node;

	//构造函数
	__list_iterator(node* t)
		:_node(t)
	{}

	//运算符重载
	T* operator->()
	{
		return &(_node->_data);
	}
}

下面我们就可以使用->来访自定义类型的内部成员了:

void test2()
{
	list l;
	A a = { 7,8 };
	l.push_back(a);
	l.push_back(a);
	l.push_back(a);
	list::iterator it = l.begin();
	while (it != l.end())
	{
		std::cout << it->_a1 << "," << it->_a2 << std::endl;
		++it;
	}
	std::cout << std::endl;
}

不过上面测试代码奇怪的是:it后面加上->构成运算符重载,该运算符返回的是T*类型的地址所以后面还要加上->才能指向所要访问的成员变量才对啊,所以不应该是it->->_a1才对吗?

这是由于编译器对代码的优化,让我们可以少写一个->就可以直接访问到成员变量了,看起来更直观。

但是下面又出现了一个问题:如果我们使用const类型的迭代器返回的应该是const T*类型才对,那我们可以模仿之前const类型迭代器的实现,再加一个模版参数Ptr来判断传入的类型是否为const:

template//增加两个Ref和Ptr模版参数来判断传入数据的类型
struct __list_iterator
{
	typedef _list_node node;
	typedef __list_iterator self;
	node* _node;

	//构造函数
	__list_iterator(node* t)
		:_node(t)
	{}

	//运算符重载
	Ref operator*()//通过第二个模版参数确定是不是const类型的返回值
	{
		return _node->_data;
	}
	Ptr operator->()//通过第三个模版参数确定是不是const类型的返回值
	{
		return &(_node->_data);
	}
};

template
class list
{
public:
	typedef _list_node node;
	//构造函数
	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}

	//迭代器
	typedef __list_iterator iterator;//第二和第三个模版参数传入T&和T*调用普通迭代器
	typedef __list_iterator const_iterator;//第二和第三个模版参数传入const T&和const T*调用const迭代器

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

private:
	node* _head;
};

 

2.3 其他功能函数的实现

我们有了迭代器就好实现其各种函数了:

2.3.1 insert

//任意位置前插入数据
void insert(iterator pos, const T& val)
{
	node* newnode = new node(val);
	newnode->_next = pos._node;
	newnode->_prev = pos._node->_prev;
	pos._node->_prev->_next = newnode;
	pos._node->_prev = newnode;
}

有了insert函数,我们刚开始搭建的push_back和push_front函数就可以直接复用了:

//尾插
void push_back(const T& val)
{
	insert(end(), val);
}
//头插
void push_front(const T& val)
{
	insert(begin(), val);
}

 

2.3.2 erase

void erase(iterator pos)
{
	assert(pos != end());//避免删除头节点
	pos._node->_prev->_next = pos._node->_next;
	pos._node->_next->_prev = pos._node->_prev;
	delete pos._node;
}

要注意了,使用erase函数后传入的迭代器会失效,因为其指向的空间会被释放。所以我们最好将erase这个函数设置一个返回值来进行修正:

iterator erase(iterator pos)
{
	assert(pos != end());
	node* next = pos._node->_next;
	pos._node->_prev->_next = pos._node->_next;
	pos._node->_next->_prev = pos._node->_prev;
	delete pos._node;
	return iterator(next);
}

有了erase函数,头删和尾删我们信手拈来:

//尾删
void pop_back()
{
	erase(--end());
}
//头删
void pop_front()
{
	erase(begin());
}

2.3.3 clean

//删除所有有效数据
void clean()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}

2.3.4 析构函数

//析构
~list()
{
	clean();
	delete _head;
	_head = nullptr;
}

2.3.5 迭代器区间构造函数

template
list(InputIterato begin, InputIterato end)//迭代器区间构造
{
	_head = new node;
	_head->_next = _head;
	_head->_prev = _head;
	while (begin != end)
	{
		push_back(*begin);
		++begin;
	}
}

2.3.6 拷贝构造函数

list(const list& val)
{
	_head = new node;
	_head->_next = _head;
	_head->_prev = _head;
	const_iterator it = val.begin();
	while (it != val.end())
	{
		push_back(*it);
		++it;
	}
}

上面代码是一个比较传统的写法,下面我们可以先复用迭代器区间构造函数创建一个临时对象,再构建一个swap函数来交换this的头节点和临时对象的头节点即可:

void swap(list& l)//交换
{
	node* tmp = _head;
	_head = l._head;
	l._head = tmp;
}
list(const list& val)
{
	_head = new node;
	_head->_next = _head;
	_head->_prev = _head;
	list tmp(val.begin(), val.end());//创建临时对象
	swap(tmp);//交换数据使this的头节点窃取临时对象的成果
}

2.3.7 operator=

list& operator=(list val)//这里传参没有使用&是为了防止对val数据的破坏
{
	swap(val);
	return *this;
}

注意上述代码传参使用&是为了让形参拷贝构造实参,防止swap函数将实参的数据进行交换

三、模拟实现list完整代码

#include
#include
namespace lhs
{
	template
	struct _list_node
	{
		_list_node* _prev;
		_list_node* _next;
		T _data;

		//构造函数
		_list_node(const T& val = T())
			:_prev(nullptr),
			_next(nullptr),
			_data(val)
		{}
	};

	template//增加两个Ref和Ptr模版参数来判断传入数据的类型
	struct __list_iterator
	{
		typedef _list_node node;
		typedef __list_iterator self;
		node* _node;

		//构造函数
		__list_iterator(node* t)
			:_node(t) 
		{}

		//运算符重载
		Ref operator*()//通过第二个模版参数确定是不是const类型的返回值
		{
			return _node->_data;
		}
		Ptr operator->()//通过第三个模版参数确定是不是const类型的返回值
		{
			return &(_node->_data);
		}
		self& operator++()//前置++
		{
			_node = _node->_next;
			return *this;
		}
		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;
		}
		self& operator-(size_t n)
		{
			for (size_t i = 0; i < n; ++i)
			{
				_node = _node->_prev;
			}
			return *this;
		}
		self& operator+(size_t n)
		{
			for (size_t i = 0; i < n; ++i)
			{
				_node = _node->_next;
			}
			return *this;
		}
		bool operator!=(self n)
		{
			return _node != n._node;
		}
		bool operator==(self n)
		{
			return _node == n._node;
		}
	};

	template
	class list
	{
	public:
		typedef _list_node node;
		//构造函数
		list()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}


		template
		list(InputIterato begin, InputIterato end)//迭代器区间构造
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			while (begin != end)
			{
				push_back(*begin);
				++begin;
			}
		}
		void swap(list& l)//交换
		{
			node* tmp = _head;
			_head = l._head;
			l._head = tmp;
		}
		list(const list& val)
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			list tmp(val.begin(), val.end());//创建临时对象
			swap(tmp);//交换数据使this的头节点窃取临时对象的成果
		}

		//运算符重载
		list& operator=(list val)//这里传参没有使用&是为了防止对val数据的破坏
		{
			swap(val);
			return *this;
		}

		//尾插
		void push_back(const T& val)
		{
			insert(end(), val);
		}
		//头插
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		//迭代器
		typedef __list_iterator iterator;//第二和第三个模版参数传入T&和T*调用普通迭代器
		typedef __list_iterator const_iterator;//第二和第三个模版参数传入const T&和const T*调用const迭代器

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

		//任意位置前插入数据
		void insert(iterator pos, const T& val)
		{
			node* newnode = new node(val);
			newnode->_next = pos._node;
			newnode->_prev = pos._node->_prev;
			pos._node->_prev->_next = newnode;
			pos._node->_prev = newnode;
		}
		//删除任意位置的元素
		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* next = pos._node->_next;
			pos._node->_prev->_next = pos._node->_next;
			pos._node->_next->_prev = pos._node->_prev;
			delete pos._node;
			return iterator(next);
		}
		//尾删
		void pop_back()
		{
			erase(--end());
		}
		//头删
		void pop_front()
		{
			erase(begin());
		}
		//删除所有有效数据
		void clean()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		//析构
		~list()
		{
			clean();
			delete _head;
			_head = nullptr;
		}
	private:
		node* _head;
	};
}

本期到这里又要结束了,感谢各位的阅览~

我们下期见~

你可能感兴趣的:(C++,c++,list,算法,数据结构)