list的模拟实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


list结构分析

在源码中可以看到list是由一个带头双向循环列表构成的,而要实现这个带头双向循环列表,首先还需要创建一个节点的类listnode,用来存储数据一次上一个节点和下一个节点的指针。而list类就存储节点的头指针。


一、listnode类的创建

template<class T>
struct list_node {
	T _data;
	list_node<T>* next;
	list_node<T>* prev;
	list_node(const T& x = T())
		: _data(x)
		, next(nullptr)
		, prev(nullptr)
	{
	}
};

节点类包括三个成员变量

data:存储数据

next:存储下一个节点的指针

prev:存储上一个节点的指针

构造函数的列表初始化把next和prev置为空指针。

二、成员函数的实现

1.构造函数
		void empty_init() {
			_head = new node;
			_head->next = _head;
			_head->prev = _head;
			
		}
		list()
		{
			empty_init();
		}
typedef list_node<T> node;

将listnode重命名位node,先开辟一个node节点当作头指针,然后让next和prev都指向head头节点,这样一个带头双向循环列表的节点就完成了。

2、迭代器的实现
template<class T,class Ref>
struct _list_iterator {
	typedef list_node<T> node;
	typedef _list_iterator<T,Ref> self;
	node* _node;
	_list_iterator(node* node)
		:_node(node)
	{}
	self& operator++() {
		_node = _node->next;
		return *this;
	}
	self& operator--() {
		_node = _node->prev;
		return *this;
	}
	Ref operator*() {
		return _node->_data;
	}
	bool operator!=(const self& s)const {
		return _node != s._node;
	}
};

list的迭代器不再是原生指针,它需要单独创建一个类来实现它作为迭代器的功能,并且list本质是链表,所以需要迭代器来进行遍历,所以要先完成迭代器的功能。

思路:

迭代器的成员变量也是一个节点node,遍历是通过节点中存储的其他节点的指针来进行遍历。迭代器需要完成两个,分别是可以修改的迭代器和不可修改的const迭代器。为了防止代码的冗余,使用类模板来完成const和非const迭代器。

构造函数使用一个node节点来进行初始化。

之后通过运算符的重载来完成迭代器的遍历。

const和非const迭代器唯一的区别就是解引用的返回值,所以只需要在解引用的返回值那使用模板即可。

typedef _list_iterator<T,T&> iterator;
typedef _list_iterator<T,const T&> const_iterator;
iterator begin() {
	return _head->next;
}
iterator end() {
	return _head;
}
const_iterator begin()const {
	return _head->next;
}
const_iterator end()const {
	return _head;
}

迭代器所必须的还有begin和end函数,也需要const和非const

begin返回哨兵位的下一个节点,end返回哨兵位就可以。

3、insert
		iterator insert(iterator pos, const T& val) {
			node* newnode = new node(val);
			node* cur = pos._node;
			node* prev = cur->prev;
			newnode->next = cur;
			cur->prev = newnode;
			newnode->prev = prev;
			prev->next = newnode;
			return newnode;
		}

插入动作是插入在传过来的迭代器的前面进行插入。

思路:

先新建一个节点,之后保存pos迭代器的节点和pos迭代器的前一个节点。将新节点进行插入连接。

4、erase
iterator erase(iterator& pos) {
	node* cur = pos._node;
	node* next = cur->next;
	node* prev = cur->prev;
	prev->next = next;
	next->prev = prev;
	delete cur;
	return next;
}

思路:

先保存pos节点和pos的上一个节点prev和下一个节点next,对next和prev、进行连接,之后释放pos节点。

5、析构函数
~list() {
	clear();
	delete _head;
	_head = nullptr;
}
void clear() {
	iterator lt = begin();
	while (lt != end()) {
		lt = erase(lt);
	}
}

思路:

析构函数使用clear函数清楚哨兵位之外的所有节点,再单独释放哨兵位就析构完成了。

clear的实现:

使用迭代器进行遍历,将除哨兵位之外的每个节点进行删除。

6、push_back
		void push_back(const T& data) {
		
			insert(end(), data);
		}

思路:

直接复用insert便可。

7、push_front
		void push_front(const T& data) {
			insert(begin(), data);
		}

思路:

直接复用insert便可。

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

思路:

直接复用erase便可。

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

思路:

直接复用erase便可。

10、operator=
		list<T>& operator=(list<T>lt) {
			swap(lt);
			return *this;
		}

思路:

使用拷贝构造传过来一个只在该函数生效的对象,与被赋值的对象lt进行交换。

		void swap(list<T>& lt) {
			node* tmp = _head;
			_head = lt._head;
			lt._head = tmp;
		}

swap只需要交换两个对象的哨兵位就可以实现交换。

测试

template<typename Container>
void print(const Container& lt) {
	typename Container::const_iterator lt1 = lt.begin();
	for (auto e : lt) {
		cout << e << ' ';
	}
}
void test2() {
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);
	lt1.push_back(6);
	print(lt1);
	list<string> lt2;
	cout << endl;
	lt2.push_back("11111111");
	lt2.push_back("22222222");
	lt2.push_back("33333333");
	lt2.push_back("44444444");
	lt2.push_back("55555555");
	lt2.push_back("66666666");
	print(lt2);
}

list的功能不仅能支持内置类型,还能够支持自定义类型。

为了防止代码的冗余,这里需要用typename来定义模板,typename是专门用于未实例化的类,能够让代码顺利通过。如果容器未实例化,迭代器是无法访问的,typename就是告诉编译器目前没有实例化,但之后会实例化,让这段代码能够通过。

运行

内置类型和自定义类型都能够使用。

全部代码

头文件

#pragma once
namespace ls {
	template<class T>
	struct list_node {
		T _data;
		list_node<T>* next;
		list_node<T>* prev;
		list_node(const T& x = T())
			: _data(x)
			, next(nullptr)
			, prev(nullptr)
		{
		}
	};
	
	template<class T,class Ref>
	struct _list_iterator {
		typedef list_node<T> node;
		typedef _list_iterator<T,Ref> self;
		node* _node;
		_list_iterator(node* node)
			:_node(node)
		{}
		self& operator++() {
			_node = _node->next;
			return *this;
		}
		self& operator--() {
			_node = _node->prev;
			return *this;
		}
		Ref operator*() {
			return _node->_data;
		}
		bool operator!=(const self& s)const {
			return _node != s._node;
		}
	};
	template<class T>
	class list {
		typedef list_node<T> node;
	public:
		typedef _list_iterator<T,T&> iterator;
		typedef _list_iterator<T,const T&> const_iterator;
		iterator begin() {
			return _head->next;
		}
		iterator end() {
			return _head;
		}
		const_iterator begin()const {
			return _head->next;
		}
		const_iterator end()const {
			return _head;
		}
		void empty_init() {
			_head = new node;
			_head->next = _head;
			_head->prev = _head;
			
		}
		list()
		{
			empty_init();
		}
		list(list<T>& tmp) {
			empty_init();
			for (auto& e : tmp) {
				push_back(e);
			}
		}
		~list() {
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear() {
			iterator lt = begin();
			while (lt != end()) {
				lt = erase(lt);
			}
		}
		void push_back(const T& data) {
		
			insert(end(), data);
		}
		void push_front(const T& data) {
			insert(begin(), data);
		}
		void pop_back() {
			erase(--end());
		}
		void pop_front() {
			erase(begin());
		}
		iterator insert(iterator pos, const T& val) {
			node* newnode = new node(val);
			node* cur = pos._node;
			node* prev = cur->prev;
			newnode->next = cur;
			cur->prev = newnode;
			newnode->prev = prev;
			prev->next = newnode;
			return newnode;
		}
		iterator erase(iterator& pos) {
			node* cur = pos._node;
			node* next = cur->next;
			node* prev = cur->prev;
			prev->next = next;
			next->prev = prev;
			delete cur;
			return next;
		}
		void swap(list<T>& lt) {
			node* tmp = _head;
			_head = lt._head;
			lt._head = tmp;
		}
		list<T>& operator=(list<T>lt) {
			swap(lt);
			return *this;
		}
	private:
		node* _head;
	};
	
	template<typename Container>
	void print(const Container& lt) {
		typename Container::const_iterator lt1 = lt.begin();
		for (auto e : lt) {
			cout << e << ' ';
		}
	}
	void test2() {
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);
		lt1.push_back(6);
		print(lt1);
		list<string> lt2;
		cout << endl;
		lt2.push_back("11111111");
		lt2.push_back("22222222");
		lt2.push_back("33333333");
		lt2.push_back("44444444");
		lt2.push_back("55555555");
		lt2.push_back("66666666");
		print(lt2);
	}

}

cpp文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include"list.h"
int main() {
	ls::test2();
}


总结

本文简单实现了list的功能,对于list有了更深入的了解。

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值