上一篇我针对不同的数据结构来演示了迭代器的使用,今天我们主要来看看STL list的使用及他的模拟实现。
首先我们来实现list节点的结构体。看下面代码:
template<class T>
struct __ListNode
{
T _data;
__ListNode* _next;
__ListNode* _prev;
__ListNode(const T& x) //节点的构造函数
:_data(x),_next(NULL),_prev(NULL)
{}
};
下面我们来看看迭代器的简单实现(注:这里只是实现他的简单基本功能,后面会进行完善)
迭代器结构中只有一个成员变量”Node* _node “(一个指向节点的指针),所以有一种说法就是说迭代器是一种对指针的封装。
template<class T,class Ref,class Ptr> //T,T&,T*
struct __ListIterator
{
typedef __ListNode<T> Node;
typedef __ListIterator<T, Ref, Ptr> self;
__ListIterator(Node* node)
:_node(node)
{}
//重载*进行解引用,所以返回指针指向的数据的引用
Ref operator*()
{
return _node->_data;
}
//重载!=,看两个迭代器是否相等,只需看他的成员变量(指向节点的指针)是否相等,即是否指向同一个节点。
bool operator!=(const self& s) const
{
return (_node != s._node);
}
bool operator==(const self& s) const
{
return (_node == s._node);
}
//重载++,就是将链表中节点的遍历访问和操作array元素时的指针接口一致。
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
/*self tmp(*this);
_node = _node->_data;
return tmp*/;
//优化
Node* cur = _node;
_node = _node->_next;
return self(cur);
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
Node* cur = _node;
_node = _node->_prev;
return self(cur);
}
Node* _node;
};
下面是STL中 , 容器list的简单实现
template<class T>
class List
{
typedef __ListNode<T> Node;
public:
typedef __ListIterator<T, T&, T*> Iterator;
List()
{
_head = GetNode(T());
_head->_next = _head;
_head->_prev = _head;
}
~List()
{
Clear();
_head = NULL;
}
Node* GetNode(const T& x) //创建新的节点
{
return(new Node(x));
}
void PushBack(const T& x)
{
Node* tmp = GetNode(x);
Node* tail = _head->_prev;
tmp->_prev = tail;
tail->_next = tmp;
tmp->_next = _head;
_head->_prev = tmp;
}
void PopBack()
{
assert(!Empty());
Node* tail = _head->_prev;
Node* prev = tail->_prev;
prev->_next = _head;
_head->_prev = prev;
delete tail;
}
void PushFront(const T& x)
{
Node* next = _head->_next;
Node* tmp = GetNode(x);
tmp->_next = next;
next->_prev = tmp;
tmp->_prev = _head;
_head->_next = tmp;
}
void PopFront()
{
assert(!Empty());
Node* tail = _head->_next;
Node* next = tail->_next;
_head->_next = next;
next->_prev = _head;
}
bool Empty()
{
return (_head->_next == _head);
}
Iterator Find(const T& x)
{
/*Node* cur = _head->_next;
while (cur != _head)
{
if (cur->_data == x)
{
return Iterator(cur);
}
cur = cur->next;
}
return _head;*/
Iterator it = Begin();
while (it != End())
{
if (*it == x)
{
return it;
}
it++;
}
return it;
}
//随机插入节点,是在给定位置pos后面插入
void Insert(Iterator pos,const T& x)
{
assert(pos != End());
Node* tail = GetNode(x);
Node* next = (pos._node)->_next;
Node* prev = (pos._node);
tail->_next = next;
next->_prev = tail;
tail->_prev = prev;
prev->_next = tail;
}
Iterator Erase(Iterator pos)
{
assert(pos != End() && pos._node);
Node* prev = (pos._node)->_prev;
Node* next = (pos._node)->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return Iterator(next);
}
void Clear()
{
Iterator it = Begin();
while (it != End())
{
Node* cur = it._node;
++it;
delete cur;
}
_head->_next = _head;
_head->_prev = _head;
}
Iterator Begin()
{
return Iterator(_head->_next);
}
Iterator End()
{
return Iterator(_head);
}
private:
Node* _head;
};
下面我们来看一个问题:迭代器失效
向容器(list、vector等)中添加元素或删除元素的操作都可能会使指向容器元素的指针、引用或迭代器失效。这里只说list。
- 向list添加元素后:
指向list的迭代器仍有效。即向list中添加元素不影响迭代器失效。 - 从list删除元素后:
当我们从list删除一个元素后,指向该元素的迭代器会失效,其他位置不受影响。
看下面两段代码,都属于正确用法:
List<int>::Iterator it = l.Begin();
while (it != l.End())
{
if ((*it)%2)
{
it = l.Erase(it);返回删除元素的下一个元素的迭代器
}
else
{
it++;
}
}
List<int>::Iterator it = l.Begin();
while (it != l.End())
{
if ((*it)%2)
{
l.Erase(it++);//删除it指向的元素后,迭代器it++
}
else
{
it++;
}
}
在调用Erase()后,不必递增迭代器,因为Erase返回的迭代器已经指向链表中的下一个元素。如果没有调用Erase(),则需要递增迭代器一次,使之移至下一个元素。所以这两句代码使用原则是:每次循环,有你没我。
思考一下:
如果使用方法如下,该如何解决迭代器失效问题:
List<int>::Iterator it = l.Begin();
while (it != l.End())
{
if ((*it)%2)
{
l.Erase(it);
}
it++;
}