1.基础概念介绍
我们的list类本质上来说是一个双向循环列表的结果,之前在讲解数据结构时有篇文章就是介绍这个的,不熟悉的同学可以去看看。当然在c++定义的库中还有一个类叫做forward_list,它的本质是一个单链表,只能向前迭代。
双向循坏列表简图
在我们这篇介绍单列表的文章当中,我们依旧采用上篇文章的介绍方式,先看懂源码,然后介绍运用,最后在总结归纳。
2.源码解析
1.基础单个节点的组成
template<class T>
struct ListNode
{
ListNode<T>* _prev;
LsitNode<T>* _next;
T _data;
ListNode(const T& data = T())
:_prev(nullptr)
, _next(nullptr)
, _data(data)
{
}
};
2.迭代器的封装操作以及一些迭代器需要用到的运算符重载
这里其实有一些问题需要思考,在这里我是否需要用重新写一个深拷贝?
其实并不需要,其本质就是开一个空间放单个节点的地址,而我们的浅拷贝已经可以满足需求了。
还有一个是是否需要重写析构函数的问题:
在这样的一个定义并封装了节点的结构体中,并没有发生动态内存规划,所以我们不需要重新定义析构函数,用系统给出的就绰绰有余了。
当然这里为什么要tyoedef一个self并且给出了3个不同的模板参数,我们将在下面的代码中详细讲解
这里姑且就认为 T为本身 ,Ref为其引用,Ptr为其指针变量
template<class T, class Ref, class Ptr>
struct _list_iterator
{
typedef ListNode<T> Node;
typedef __list_iterator<T, Ref, Ptr> self;
Node* _node;
_list_iterator(Node* x)
:_node(x)
{
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->() //返回了节点的地址
{
return &_node->_data; //这里是因为系统会省略一个->(接收到这个地址后用了->原来的用法,然后指向了存的数据)
}
self& oeprator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)//后置++加一个int占位//这里不用tmp是因为它是由我们创造的一个临时变量,会销毁
{
self tmp(*this); //this这里接收的是迭代器的地址,然后解引用找到了它的迭代器,然后在拷贝构造。
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator != (const self& it)const
{
return _node != it._node;
}
bool operator == (const self& it)const
{
return _node == it._node;
}
}; template<class T, class Ref, class Ptr>
struct _list_iterator
{
typedef ListNode<T> Node;
typedef __list_iterator<T, Ref, Ptr> self;
Node* _node;
_list_iterator(Node* x)
:_node(x)
{
}
};
其实实质上,我们这些重载符号是应用在迭代器类型创造的对象,那为什么我们只用list类创造的对象后还能使用呢?那么我们不妨来看看list的类的一些封装细节。
3.正主list的封装以及一些细节的讲解
template<class T>
class list
{
private:
typedef ListNode<T> Node;
Node* _head;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;//说白了就是我返回的时候会用这三种类型的,我在这里这么写就是为了下面可以不用这么写
// 而且当你使用的时候需要表明这三种类型然后写个迭代器会非常麻烦,所以我在这里进行了这样的操作
typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
typedef reverse_iterator<iterator, T&, T*> reverse_iterator;
};
4.对迭代器有关的一系列的函数
这里从某种角度来讲设计者想要保持迭代的一种对称性,所以head一开始其实是一个哨兵位。
这边能这么写是因为上面的typedef,让一个类有了多种可能,是一种我们来处理这类问题的惯长写法
iterator begin()
{
return iterator(_head->_next); //因为返回的是迭代器
}
iterator end()
{
return iterator(_head);
}
reserve_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
5.list的构造函数
list()//无参时
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
list(int n , const T& val = T())
{ //先初始化一个哨兵位结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//开始插入
for(i = 0, i < n, i++)
{
push_back(val);
}
}
list(size_t n , const T& val = T())
{ //先初始化一个哨兵位结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//开始插入
for(i = 0, i < n, i++)
{
push_back(val);
}
}
template<class InputIterator>
list(InputIterator first, InputIterator last)//接收器迭代器的begin和end,实际收到的也是迭代器本身
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
while (first != last)
{
push_back(*first);
++first;
}
}
6.list的析构函数
list(const list<T>& lt)
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
list<T> tmp(lt.begin(), lt.end());
std::swap(_head,tmp._head);
}
7.赋值构造
list<T>& operator=(list<T> lt)
{
std::swap(_head, lt._head);
return *this;
}
8.析构函数
~list()
{
clear();
delete _head;
_head = nullptr;
}
9.函数全部清除clear
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
10.前插后插前删后删
这里直接调用insert和erase就行
push_back
push_front
pop_back
pop_front
11.insert实现
其实最主要就是找先拿一个prev和cur保存然后按班就步就行
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//开始换节点里的地址
prev->_data = newnode;
newnode->_next = cur;
newnode->_prev = prev;
cur->_prev = newnode;
return iterator(newnode);
}
12.erase的实现
iterator erase(iterator pos)
{
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
3.一些常见的实战运用
// list的构造
void TestList1()
{
list<int> l1; // 构造空的l1
list<int> l2(4, 100); // l2中放4个值为100的元素
list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
list<int> l4(l3); // 用l3拷贝构造l4
// 以数组为迭代器区间构造l5
int array[] = { 16,2,77,29 };
list<int> l5(array, array + sizeof(array) / sizeof(int));
// 列表格式初始化C++11
list<int> l6{ 1,2,3,4,5 };
// 用迭代器方式打印l5中的元素
list<int>::iterator it = l5.begin();
while (it != l5.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// C++11范围for的方式遍历
for (auto& e : l5)
cout << e << " ";
cout << endl;
}
迭代器的使用
void PrintList(const list<int>& l)
{
// 注意这里调用的是list的 begin() const,返回list的const_iterator对象
for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
{
cout << *it << " ";
// *it = 10; 编译不通过
}
cout << endl;
}
void TestList2()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 使用正向迭代器正向list中的元素
// list<int>::iterator it = l.begin(); // C++98中语法
auto it = l.begin(); // C++11之后推荐写法
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;