list容器模拟实现

📋 个人简介

  • 💖 作者简介:大家好,我是菀枯😜

  • 🎉 支持我:点赞👍+收藏⭐️+留言📝

  • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

    v2-af3cfbda99d44f8eb114cc6c74c92253_720w

前言

好久不见啊!今天我们的任务是自己从头到尾实现一个STL中的list容器。

list的实现

list的介绍

之前我们实现了STL中的vector容器,vector是一个变长数组。而list在使用上和vector相似,都是把我们的数据按顺序存储下来。但二者底层的数据结构有所区别,list的底层数据结构是双向循环带头链表。关于list的接口使用大家去看看文档就好std::list - cppreference.com

list节点的实现

首先我们要清楚list和list的节点这是两个东西,我们需要分别进行设计。

template<typename T>
struct listNode
{
	listNode<T>* _pre; //指向前一个节点的指针
	listNode<T>* _next; //指向后一个节点的指针
	T _data; //存放的数据

	listNode(const T& val = T())
		:_pre(nullptr)
		, _next(nullptr)
		, _data(val)
	{}
};

list迭代器的实现

我们在实现vector的迭代器时使用了原生指针,而因为list的节点并不能保证是连续存在的,所以我们没办法再使用原生指针作为list的迭代器。那么我们该怎么去设计它呢?

首先我们需要找到对应节点,所以在迭代器中需要一个指针来指向这个节点。然后这个迭代器要能对这个节点进行正确的操作,所以我们要为这个迭代器编写出对应的方法。最后因为STL中的list为一个双向带头循环链表,所以我们的迭代器也应该要支持前后的移动。

接下来我们来看看代码怎么写:

template<typename T, typename Ref, typename Ptr>
struct _list_iterator
{
	typedef listNode<T> Node;
	typedef _list_iterator<T, Ref, Ptr> self;
	Node* _node; //指向对应节点的指针

	_list_iterator(Node* node = nullptr)
		:_node(node)
	{}
	//节点的获取
	Ref operator*()
	{
		return _node->_data;
	}

	Ptr 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->_pre;
		return *this;
	}

	self operator--(int)
	{
		self tmp(*this);
		_node = _node->_pre;
		return tmp;
	}
	//迭代器的关系运算
	bool operator!= (const self& it)
	{
		return _node != it._node;
	}

	bool operator==(const self& it)
	{
		return _node == it._node;
	}
};

list的结构

我们已经做好了所有的准备工作,接下来我们该想想如何去维护这个双向带头循环链表了。

其实我们只需要一个指针就可以去维护这个链表,我们只需要一个指针去指向头,我们就可以找到这个链表中的其他所有节点了。

template<typename T>
class list
{
public:
	typedef listNode<T> Node;
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;
private:
	Node* _head;
    ...

list的迭代器接口

首先我们要实现的应该就是begin()和end(),找到链表的首尾之后才方便我们进行其他的操作。想想我们现在有什么,好像只有一个指向头节点的指针,如何用这个指针来找链表的头尾呢?再来看看这个结构。

image-20221209174604803

我们发现头节点的下一个节点就是begin,而头节点的上一个指针就是end,那么这两个接口就很简单了。

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

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

image-20221209180430198

在这里有的朋友可能会疑惑为什么end()是用头指针来进行构造?因为在STL中的接口都采取了左闭右开的设计方式。如果我们使用head->next来构造end接口的话,会导致我们无法访问最后一个接口。

1670579449089

然后我们来实现剩余的接口

size_t size()
{
    size_t num = 0;
    iterator it = begin();
    //从头结点开始往后一个一个数
    while (it != end())
    {
        ++num;
        ++it;
    }
    return num;
}

//如果为空那么头结点的下一个也为头节点
bool empty()
{
    return _head == _head->_next;
}
//T为元素类型
T &front()
{
    assert(!empty());
    return *begin();
}

T &back()
{
    assert(!empty());
    return *(--end());
}

list的元素操作

插入操作

首先我们来写一下可以在指定位置进行插入的insert接口

iterator insert(iterator pos, const T &value)
{
    Node *cur = pos._node;//指向当前节点
    Node *pre = cur->_pre;//指向前一个节点
    Node *newNode = new Node(value);

    newNode->_pre = pre;
    newNode->_next = cur;
    cur->_pre = newNode;
    pre->_next = newNode;
    return iterator(newNode);
}

动画如下:
动画

有了insert,push_front和push_back直接套用insert就行

void push_back(const T &val)
{
    insert(end(), val);
}
void push_front(const T &val)
{
    insert(begin(), val);
}
删除操作

首先我们来写一下可以在指定位置进行删除的erase接口

iterator erase(iterator pos)
{
    assert(!empty());
    Node *cur = pos._node;
    Node *pre = cur->_pre;
    Node *next = cur->_next;

    delete cur;
    pre->_next = next;
    next->_pre = pre;
    return iterator(next);
}

动画如下:

动画

有了erase,pop_front和pop_back直接套用insert就行

void pop_back(const T &val)
{
    insert(--end());
}
void pop_front(const T &val)
{
    insert(begin());
}

void clear()
{
    iterator tmp = begin();
    while (tmp != end())
    {
        tmp = erase(tmp);
    }
}

list的构造和析构函数

构造函数

我在这里也实现了三个使用较多的接口

void empty_inti()
{
    _head = new Node();
    _head->_next = _head;
    _head->_pre = _head;
}

//默认构造函数 构造一个空链表
list()
{
    empty_inti();
}
//迭代器区间构造
template <typename InputIterator>
list(InputIterator first, InputIterator last)
{
    empty_inti();
    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}

//给值构造
list(int n, const T &value = T())
{
    empty_inti();

    while (n--)
    {
        push_back(T);
    }
}
析构函数

调用clear清空链表后,将new出来的头节点删除即可

~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

完整代码

#include <assert.h>

template<typename T>
struct listNode
{
	listNode<T>* _pre;
	listNode<T>* _next;
	T _data;

	listNode(const T& val = T())
		:_pre(nullptr)
		, _next(nullptr)
		, _data(val)
	{}
};

template<typename T, typename Ref, typename Ptr>
struct _list_iterator
{
	typedef listNode<T> Node;
	typedef _list_iterator<T, Ref, Ptr> self;
	Node* _node;

	_list_iterator(Node* node = nullptr)
		:_node(node)
	{}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr 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->_pre;
		return *this;
	}

	self operator--(int)
	{
		self tmp(*this);
		_node = _node->_pre;
		return tmp;
	}

	bool operator!= (const self& it)
	{
		return _node != it._node;
	}

	bool operator==(const self& it)
	{
		return _node == it._node;
	}
};

template<typename T>
class list
{
public:
	typedef listNode<T> Node;
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;

private:
	Node* _head;
public:
	list()
	{
		_head = new Node();
		_head->_next = _head;
		_head->_pre = _head;
	}

	void swap(list<T>& l)
	{
		std::swap(_head, l._head);
	}

	template<typename InputIterator>
	list(InputIterator first, InputIterator last)
	{
		empty_inti();
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

	size_t size()
	{
		size_t num = 0;
		iterator it = begin();
		while (it != end())
		{
			++num;
			++it;
		}
		return num;
	}

	bool empty()
	{
		return _head == _head->_next;
	}

	void empty_inti()
	{
		_head = new Node();
		_head->_next = _head;
		_head->_pre = _head;
	}

	list(int n, const T& value = T())
	{
		empty_inti();

		while (n--)
		{
			push_back(T);
		}
	}

	list(const list<T>& lt)
	{
		empty_inti();
		list<T> tmp(lt.begin(), lt.end());
		swap(tmp);
	}

	~list()
	{
		clear();
		delete _head;
		_head = nullptr;
	}

	list& operator=(list lt)
	{
		empty_inti();
		swap(lt);
	}

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

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

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

	const_iterator end() const
	{
		return _head;
	}

	T& front()
	{
		assert(!empty());
		return _head->_next->_data;
	}

	T& back()
	{
		assert(!empty());
		return _head->_pre->_data;
	}

	const T& back() const
	{
		assert(!empty());
		return _head->_pre->_data;
	}

	const T& front() const
	{
		assert(!empty());
		return _head->_next->_data;
	}

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

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

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

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

	void clear()
	{
		iterator tmp = begin();
		while (tmp != end())
		{
			tmp = erase(tmp);
		}
	}

	iterator insert(iterator pos, const T& value)
	{
		Node* cur = pos._node;
		Node* pre = cur->_pre;
		Node* newNode = new Node(value);

		newNode->_pre = pre;
		newNode->_next = cur;
		cur->_pre = newNode;
		pre->_next = newNode;
		return iterator(newNode);
	}

	iterator erase(iterator pos)
	{
		assert(!empty());
		Node* cur = pos._node;
		Node* pre = cur->_pre;
		Node* next = cur->_next;

		delete cur;
		pre->_next = next;
		next->_pre = pre;
		return iterator(next);
	}
};

结语

1647941444633

欢迎各位参考与指导!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

。菀枯。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值