目录
前言
我们知道,stl中的vector对应数据结构中的顺序表,string类对应字符串,而今天要讲的list类对应带头双向链表,并不是对应单链表,带头双向链表的基本操作在数据结构课程中已经学过,所以今天即将要讲的常见接口并不是重点,重点是list的迭代器的实现。
我们也知道,string、vector的迭代器就是原生指针,如果使用原生指针可以实现list的迭代器吗?答案是不行,因为list的数据并不是一块连续的存储空间,无法像指针那样取访问元素,但是为了保持所有容器迭代器的使用一致,我们如何实现list的迭代器才能像原生指针那样通过++、--去控制,这里就体现出了封装的重要性,快往下看看吧!
重要接口实现
-
框架
代码:
template <class T>
struct ListNode
{
//构造函数用来创节点
ListNode(const T& x = T())
:_data(x)
, _prev(nullptr)
, _next(nullptr)
{
}
T _data;
ListNode<T>* _prev;
ListNode<T>* _next;
};
template <class T>
class List
{
typedef ListNode<T> Lnode; //作用:下面写ListNode<T>较麻烦,typedef一下,使用Lnode较方便
public:
//...
private:
Lnode* _head;
};
-
默认成员函数
代码:
//创建并初始化头节点,放于构造函数的最前面
void empty_init()
{
_head = new Lnode();
_head->_next = _head->_prev = _head;
}
//构造函数
List()
{
empty_init();
}
//普通拷贝构造函数
List(const List& L) //注意:类外必须是List<类型名>,类里可以是List,但建议List<T>
{
empty_init();
auto it = L.begin();
while (it != L.end())
{
push_back(*it);
it++;
}
}
void swap(List<T>& L)
{
std::swap(_head, L._head);
}
//传迭代器区间构造函数
template <class InputIterator>
List(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
//拷贝构造函数
//现代写法
List(const List<T>& L)
{
empty_init();
List<T> tmp(L.begin(), L.end());
swap(tmp);
}
//赋值运算符重载
//现代写法
List<T>& operator=(const List<T>& L)
{
List<T> tmp(L.begin(), L.end());
swap(tmp);
return *this;
}
//更狠的现代写法
List<T>& operator=(List<T> L) //直接传一个拷贝过来,相当于上面的tmp,函数结束自动释放
{
swap(L);
return *this;
}
//清除除了头节点之外所有的节点
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);
}
}
//析构函数
~List()
{
clear();
_head->_next = _head->_prev = nullptr;
delete _head;
_head = nullptr;
}
-
迭代器(重点)
1.引言
2.list迭代器类实现
1)框架
代码:
template <class T, class Ref, class Ptr>
struct __list_iterator
{
//注意:这两个typedef只是因为ListNode<T>、__list_iterator<T, Ref, Ptr>很麻烦写,所以简化一下,也方便理解
typedef ListNode<T> Lnode;
typedef __list_iterator<T, Ref, Ptr> iterator;
//构造函数
__list_iterator(Lnode* p)
:_node(p)
{
}
//...
Lnode* _node; //链表中的迭代器表示节点的指针
};
2) 关系运算符重载
代码:
bool operator==(const iterator& it) const
{
return _node == it._node;
}
bool operator!=(const iterator& it) const
{
return !(*this == it);
}
3)运算符++、--重载
代码:
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
4)操作符*重载
代码:
Ref operator*()
{
return _node->_data;
}
5)操作符->重载
代码:
Ptr operator->()
{
return &(_node->_data);
}
测试:
3.list类中调用实现
代码:
typedef __list_iterator<T, T&, T*> iterator; //普通迭代器类
typedef __list_iterator<T, const T&, const T*> const_iterator; //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);
}
-
增删查改
代码:
iterator insert(iterator pos, const T& x)
{
Lnode* newNode = new Lnode(x);
pos._node->_prev->_next = newNode;
newNode->_prev = pos._node->_prev;
newNode->_next = pos._node;
pos._node->_prev = newNode;
return iterator(newNode); //返回插入位置的迭代器
}
//尾插
void push_back(const T& x)
{
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
iterator erase(iterator pos)
{
assert(pos != end());
Lnode* tmp = pos._node->_next;
pos._node->_prev->_next = pos._node->_next;
pos._node->_next->_prev = pos._node->_prev;
delete pos._node;
return iterator(tmp);
}
//尾删
void pop_back()
{
erase(--end());
}
//头删
void pop_front()
{
erase(begin());
}
后记
在list类的实现中,默认成员函数、操作符重载以及增删查改已不再是重点,重点是掌握迭代器的实现,因为与string、vector中的迭代器实现不同,也没有那么简单,上面总结的很清楚,不懂的可以私我或者写在评论区,加油,拜拜!