目录
一.什么是反向迭代器
首先我们都知道迭代器,范围for里经常用与遍历。我们在模拟实现vector等容器的时候经常把它当成原生指针来看待。反响迭代器其实故名思意,起作用和迭代器相反。遍历的时候也是倒着遍历。
二.反向迭代器的底层
首先确定的是我们肯定需要用模板来实现反响迭代器。因为传入的类型不确定。当我们究竟应该用什么来构建反向迭代器呢。虽然不同容器的迭代器的底层实现是不同的,但迭代器的功能是一样的,我们可以通过迭代器来构造反响迭代器,并通过复用迭代器的函数大幅度减少我们的工作量。
三.模拟实现
因为反响迭代器和正向迭代器一样,也需要实现解引用等功能,所以会有返回数据引用或指针的情况,但由于我们并不知道数据的具体类型,所以我们可以通过模板将数据传入,并在实例化的受处理这个问题。
template<class Iterator, class Ref, class Ptr>
class _reverse_iterator
{
typedef _reverse_iterator<Iterator, Ref, Ptr> Rvi;
private:
Iterator _it;
};
1.构造函数
我们应该通过迭代器来构造反向迭代器,而迭代器是拥有构造函数的,所以我们可以直接这样写
_reverse_iterator(Iterator it)
:_it(it)
{}
2.解引用
其实直接调用正向迭代器的解引用函数就可以,但是在stl库里为了内部函数对应,他会先自减在解引用,因为我们知道迭代器的begin指向数据的开始,末尾指向最后一个数据的下一个位置。如果我们把反向迭代器的起始位置放在最后一个数据的下一个位置,也就是其实数据的上一个位置,这样刚好可以和正向迭代器的位置对称。
由于指向的位置是要求位置的下一个,所以我们要先自减在解引用。
Ref operator*() //为了和正向迭代器对称,本质还是复用正向迭代器的底层函数
{
Iterator tmp = _it;
tmp--;
return *tmp;
}
3.箭头操作符
我们可以复用我们已经写过的函数,箭头操作符需要找到当前元素的地址,在外层通过这个地址调用系统的箭头操作符找到对应元素。这个稍微有点难理解的点在于,编译器会对其进行优化导致会隐藏一个箭头。
Ptr operator->()
{
//return _cur.operator->(); 两种写法都可以,这种写法是调用正向迭代器的箭头重载
return &(operator*()); // 因为是箭头所以需要当前位置的地址,拿到当前位置的地址,在使用时在调用系统内部的箭头,在使用时其实有两个箭头,但会优化掉一个,一个是重载,一个是系统调用的箭头,
}
4.自增自减操作符
直接调用正向迭代器的自增自减就可以。
Rvi& operator++()
{
_it--;
return *this;
}
Rvi& operator--()
{
_it++;
return *this;
}
五.在具体案例的使用
在list或者vector中内部实现如下图所示。在需要调用反向迭代器的时候直接调用反向迭代器的对应函数即可
typedef T* iterator;
typedef const T* const_iterator;
typedef _reverse_iterator<iterator, T&, T*> reverse_iterator;
typedef _reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}