目录
前言
特点
注意事项
实现
构造函数
功能函数
在list与vector中的使用
vector
list
前言
反向迭代器是一种在序列容器的末尾开始,并向前移动至序列开始处的迭代器。在C++中,反向迭代器由标准库中的容器类提供,比如vector
、list
、deque
等。它们允许程序员以逆序遍历容器中的元素。
以下是反向迭代器的一些特点:
特点
- 反向遍历:反向迭代器从序列的最后一个元素开始,逐步移动到第一个元素。
- 操作符重载:C++中的反向迭代器重载了递增(
++
)和递减(--
)操作符。递增操作使迭代器向序列的开始方向移动,而递减操作使其向序列的结束方向移动。 - 类型:反向迭代器的类型通常由容器类型加上
reverse_iterator
后缀表示,例如vector<int>::reverse_iterator
。
注意事项
- 反向迭代器不支持所有的普通迭代器的操作,例如算术操作(加、减)。
- 使用反向迭代器时,递增操作实际上是向序列的开始方向移动。
- 在C++标准库中,并不是所有容器都支持反向迭代器。只有那些支持双向迭代器(
BidirectionalIterator
)或随机访问迭代器(RandomAccessIterator
)的容器才提供反向迭代器。这是因为反向迭代器需要能够向前和向后遍历容器,而这两种迭代器都支持这些操作。
需要逆序访问容器元素时,它们可以简化代码并提高效率
反向迭代器有const版本和非const版本,所以我们需要实现两个版本。
实现
反向迭代器由于与正向迭代器的行为相似,因此借鉴适配器的思想,用正向迭代器实现反向迭代器
同时我们增加两个模板参数Ref Ptr,作为const T&和Const T*的区分
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
成员变量就是一个被适配的正向迭代器
private:
Iterator _it;
构造函数
ReverseIterator(Iterator it)
:_it(it)
{}
用传入的模板迭代器去初始化成员
功能函数
++ --
Self& operator++()
{
--_it;
return *this;
}
Self& operator--()
{
++_it;
return *this;
}
* ->两种解引用
为了实现对称,解引用时,解引用的是当前位置的下一个数据
Ref operator*() //内部去调用普通迭代器的解引用
{
Iterator cur = _it;
return *(--cur); //返回数据的引用,不能--_it,防止迭代器错位
}
//->也是一种解引用
Ptr operator->() //返回的其实是一个指针
{
return &(operator*());
}
== !=
迭代器的比较,看看成员参数是不是一个
bool operator!=(const Self& s)
{
return _it != s._it;
}
bool operator==(const Self& s)
{
return _it == s._it;
}
在list与vector中的使用
vector
首先我们需要展开头文件
#include "reverse_iterator.h" //反向迭代器头文件在此展开
然后利用typedef将迭代器重命名
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;
rbegin与rend
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
其中const反向迭代器将调用const成员(end()、begin())
((((
题外话:
返回时,采用的是传值返回
自定义类型的传值返回通常是通过拷贝构造函数来实现的。当一个对象作为函数的返回值时,如果采用值返回的方式,函数内部会创建一个临时对象,这个临时对象是通过拷贝构造函数来初始化的,它是对返回对象的一个副本。这个副本具有常性(体现在引用时)
这里一个特例,这是一个匿名对象,具有常性,但是却可以调用非const成员函数。
示例:
class MyClass {
public:
MyClass() {
// 构造函数
}
MyClass(const MyClass& other) {
// 拷贝构造函数
}
// 其他成员函数和成员变量...
};
MyClass createObject() {
MyClass obj;
// 对obj进行一些操作
return obj; // 这里会调用拷贝构造函数来构造返回值
}
int main() {
MyClass result = createObject(); // 接收返回值,同样会调用拷贝构造函数
return 0;
}
在上述代码中,当createObject
函数返回obj
时,会调用MyClass
的拷贝构造函数来构造一个临时对象,这个临时对象随后会被用来初始化main
函数中的result
对象。因此,在这个过程中至少会发生两次拷贝构造:一次是在函数返回时构造临时对象,另一次是在接收返回值时。
需要注意的是,现代编译器通常会对此类操作进行优化,比如返回值优化(NRVO,Named Return Value Optimization)或者拷贝省略(copy elision),从而避免不必要的拷贝,以提高性能。在C++11及以后的版本中,这种优化是被标准所允许的,甚至在某些情况下是强制的。
示例2:
iterator begin() { return iterator(_head->_next); }
产生临时对象后:
这个临时对象会在表达式结束时被销毁,但是因为这是在一个返回语句中,所以返回的对象会被用来初始化函数调用的结果。这样,当函数调用者接收这个返回值时,他们实际上是在接收一个复制构造的 iterator
临时对象,而不是原始的返回对象。接收时,会使用拷贝构造等手段完成接收。
))))
list
同样得展开头文件
#include "reverse_iterator.h" //对应头文件内容在此展开
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, 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这个模板类已经在此文件中展开,因此在list类中可以直接使用这个类模板,并借助自身的成员去实例化这个模板类。
reverse_iterator rbegin()
{ //强调对称
return reverse_iterator(end()); //借助end()迭代器构造反向迭代器
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const //const迭代器
{
return const_reverse_iterator(end()); //调用的是const函数end()
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}