在自主实现STL的过程中,可以看出有的就是指针,有的则可以用类实现。用法大致类似于指针,但在细节会出现差别,这就要从底层谈起。
1.string、vector、list迭代器的实现
string:
string中其实就和指针类似
vector:vector内其实也和指针类似
list:
// 同一个类模板实例化出的两个类型
// typedef __list_iterator<T, T&, T*> iterator;
// typedef __list_iterator<T, const T&, const T*> const_iterator;
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef list_node<T> node;
typedef __list_iterator<T, Ref, Ptr> Self;
node* _pnode;//编译器自动生成的析构函数不会处理指针
__list_iterator(node* p)
:_pnode(p)
{}
Ptr operator->()
{
return &_pnode->_data;
}
// iterator it
// *it
// ++it;
Ref operator*()
{
return _pnode->_data;
}
// const iterator cit
// *cit
// ++cit 这样的话,可以解引用,但是不能++
/*const T& operator*() const
{
return _pnode->_data;
}*/
// ++it
Self& operator++()
{
_pnode = _pnode->_next;
return *this;
}
// it++
Self operator++(int)
{
Self tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
Self& operator--()
{
_pnode = _pnode->_prev;
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
bool operator!=(const Self& it) const
{
return _pnode != it._pnode;
}
bool operator==(const Self& it) const
{
return _pnode == it._pnode;
}
};
2.常见迭代器失效
(1)序列式容器(vector,string)--->insert/erase时失效
这种容器因为会出现扩容问题在insert时,所以会出现迭代器失效(其实也就是指针失效了)
//迭代器失效
iterator insert(iterator pos, const T& val)//扩容导致pos失效了,需要更新处理一下
{ //pos不用引用,因为有时会用传值返回,导致数值变化
//扩容
if (_finish == _endofstorage)
{
size_t len = pos - _start;
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
pos = _start + len;
}
//挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = val;
++_finish;
return pos;
}
iterator erase(iterator pos)//我们认为erase结束后pos失效,所以要用返回值来更新迭代器
{
assert(pos >= _start&&pos < _finish);
iterator begin = pos+1;
while (begin<_finish)
{
*(begin-1) = *(begin);
begin++;
}
_finish--;
return pos;
}
但我们可以发现在insert、erase时返回值返回正确迭代器,故我们可以在使用insert、erase后更新迭代器即可以继续使用。
(2)关联式迭代器失效(如map, set,multimap,multiset)--->erase时失效
iterator erase(iterator pos)
{
assert(pos != end());
node* prev = pos._pnode->_prev;
node* next = pos._pnode->_next;
prev->_next = next;
next->_prev = prev;
delete pos._pnode;
--_size;
return iterator(next);
}
在这里我们可以看出,原来pos指向的位置被删除了,只要在函数内将迭代器更新并返回迭代器即可以避免报错。在1.使用完erase后即时将迭代器更新,或者在2.使用erase时进行迭代器后置++也可以避免错误。