1、list的迭代器底层实现
用一个类型去封装结点的指针,构成一个自定义类型,然后重载*和++等运算符,使其可以像指针一样进行使用。
2、模拟实现list
刚开始实现的初步工作就是实现一个双向循环链表,并且指向自己。
(1)结点类型定义
//结点类型
template<class T>
struct _list_node
{
_list_node<T>* _next;
_list_node<T>* _prev;
T _data;
_list_node(const T& x=T())//注意这里的无参构造
:_data(x)
, _next(nullptr)
, _prev(nullptr)
{}
};
(2)迭代器实现
//迭代器
//_list_iterator<T,T&,T*>
//_list_iterator<T,const T&,const T*>
template<class T,class Ref,class Ptr>
struct _list_iterator
{
typedef _list_node<T> Node;
typedef _list_iterator<T, Ref, Ptr> Self;
Node* _node;
_list_iterator(Node* node)//封装一个节点类型的指针让其成为迭代器
:_node(node)
{}
//*it
Ref operator*()//返回值是T&或constT&
{
return _node->_data;
}
//++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
//it++
Self operator++(int)
{
Self tmp(*this);
++(*this);
return tmp;
}
//--
Self operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(int)
{
Self tmp(*this);
-- (*this);
return tmp;
}
//!=
bool operator!=(const Self& it)//传进来的是一个迭代器
{
return _node != it._node;
}
//->
Ptr operator->()//返回类型是T*或const T*
{
return &_node->_data;
}
}
- 通过设计三个参数,来区分const_iterator和普通Iterator;且实现就是将结点类型的指针进行封装,使其具有指针的基本功能即可。
->重载的补充
(3)list内的begin()和end()
template<class T>//带头双向循环链表
class list
{
typedef _list_node<T> Node;
public:
typedef _list_iterator<T,T&,T*> iterator;//普通迭代器
typedef _list_iterator<T, const T&, const T*> const_iterator;//const迭代器
//根据传入的类型的不同,来确定是const迭代器还是普通迭代器
//要写成普通版本和const版本
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin()const//返回的是const迭代器
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
private:
Node* _head;//头结点
};
其中通过typdef来定义iterator和const_iterator,传入的类型不同即Ref和Ptr类型不同。
(4)构造函数
list()
{
_head = new Node;//申请头节点
_head->_next = _head;
_head->_prev = _head;
}
申请头节点,并且初始状态让自身的两个指针都指向自己(_prev和_next),这就是带头的双向循环链表。
(5)拷贝构造和赋值运算符重载
//拷贝构造和赋值运算符重载
list(const list<T>& it)//不存在的
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
const_iterator l = it.begin();
while (l!=it.end())
{
push_back(*l);
++l;
}
}
list<T>& operator=(const list<T>& it)//赋值运算符重载有自己的头结点
{
if (this != &it)//不用自己给自己赋值
{
clear();
for (auto &i : it)
{
push_back(i);
}
}
return *this;
}
//新写法
list<T>& operator=(list<T>& it)
{
swap(_head, it._head);
return *this;
}
(1)拷贝构造是一个构造过程,这个对象还不存在,所以可以
_head=new Node; _head->_next = _head; _head->_prev = _head;
先新建一个带头双向循环链表,再进行每个节点的值的拷贝插入。
(2)赋值运算符重载,是在两个对象都已经构造好都有(或一个有一个没有)一定的元素的情况下用到的。这个时候只需要清空要拷贝数据的list,然后再将另一个的数据完全插入进去即可。
(3)赋值运算符的新写法解析:
list<T>& operator=(list<T>& it)
{
swap(_head, it._head);
return *this;
}
传入的是一个临时变量,交换了需要被赋值的list和传入变量的的头结点的指向,交换完成后,原传入的临时变量带着原来_head的指向空间被销毁,留下来的是赋值成功的list。
(6)insert
void insert(iterator pos, const T& x)//在pos位置前进行插入
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
(7)erase
void erase(iterator pos)
{
assert(pos != end());//头节点不能删
Node* cur = pos._node;
Node*prev = cur->_prev;
Node*next = cur->_next;
delete cur;
prev->_next = next;
next->_prev = prev;
}
(8)push_back/push_front/pop_back/pop_front
//push_back
void push_back(const T& x)
{
/*Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;*/
insert(end(), x);
}
void pop_back()
{
//erase(iterator(_head->_prev));
erase(--end());
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
因基本逻辑相同,所以这里利用insert和erase实现,提高代码复用率
(9)析构函数 和 clear()
~list()
{
clear();
delete _head;
_head = nullptr;
}
//clear
void clear()
{
iterator it = begin();
while (it!=end())
{
erase(it++);
}
}
clear()只留头结点,而析构函数最后也要将头结点销毁掉。