📋 个人简介
-
💖 作者简介:大家好,我是菀枯😜
-
🎉 支持我:点赞👍+收藏⭐️+留言📝
-
💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️
前言
好久不见啊!今天我们的任务是自己从头到尾实现一个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(),找到链表的首尾之后才方便我们进行其他的操作。想想我们现在有什么,好像只有一个指向头节点的指针,如何用这个指针来找链表的头尾呢?再来看看这个结构。
我们发现头节点的下一个节点就是begin,而头节点的上一个指针就是end,那么这两个接口就很简单了。
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
在这里有的朋友可能会疑惑为什么end()是用头指针来进行构造?因为在STL中的接口都采取了左闭右开的设计方式。如果我们使用head->next来构造end接口的话,会导致我们无法访问最后一个接口。
然后我们来实现剩余的接口
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);
}
};
结语
欢迎各位参考与指导!!!