作者前言:学习迭代器之前,我们要先清楚,什么是迭代器?迭代器是干什么用的?为什么要使用迭代器?
迭代器是一个支持指针类型抽象的类对象。提供了一种一般化的方法,对顺序或关联容器类型中的每个元素进行连续访问。
简单来说,迭代器就是用来遍历容器的工具,并可以对容器进行一定的操作。
划重点!!!
迭代器和指针有什么区别?
迭代器不是指针,是类模板,表现的像指针。他只是模拟了指针的一些功能,通过重载了指针的一些操作符 *、++、--、&等。迭代器封装了指针,是一个可遍历STL容器内全部或部分元素的对象,本质是封装了原生指针,是指针概念的一种提升,提供了比指针更高级的行为,相当于一种智能指针。
迭代器返回的是对象引用而不是对象的值,所以const只能输出迭代器使用*取值后的值而不能直接输出其自身。
或许会有人说:我不用迭代器也一样可以遍历容器,为什么还要用迭代器?
我们现在常用的容器vector,list,map,set,multiset,multimp,deque这几个容器的内部实现由顺序表,链表,红黑树。所以我们要遍历这些容器就要先知道它的底层结构,这样就很麻烦。而iterator就是在底层定义好,任何容器都是同样的遍历方式,这样就算我们不知道底层实现也可以操作容器。
所以Iterator的使用可以让我们在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。
迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型有iterator、const_iterator、reverse_iterator和const_reverse_iterator。
举例讲解迭代器:
假设iter为任意容器类型的一个iterator,则:
++iterator;//向前移动迭代器,使其指向容器的下一个元素
*iterator;//返回iterator指向元素的值
每种容器类型都提供一个begin()和一个end()成员元素。
begin()返回一个iterator,它指向容器的第一个元素
end()返回一个iterator,它指向容器的末元素的下一个位置
由于模板和嵌套类语法的原因 iterator 的定义看起来有点吓人,例如下面是一对 iterator的定义 它们指向一个内含 string 元素的 vector:
// vector<string> vec;
vector<string>::iterator iter = vec.begin();
vector<string>::iterator iter_end = vec.end();
为了把每个 string 元素打印到标准输出上 我们可以这样写
for( ; iter != iter_end; ++iter )
cout << *iter << '\n';
当然,这里 *iter 的运算结果就是实际的 string 对象。
除了iterator类型,每个容器还定义了一个 const iterator类型,对于遍历const容器是必需的,const iterator 允许以只读方式访问容器的底层元素。分析下面给出的一个例子
// 无法通过编译
template < typename type >
int count( const vector< type > &vec, type value )
{
int count = 0;
vector< type >::iterator iter = vec.begin();
while ( iter != vec.end() )
{
if ( *iter == value )
++count;
++iter;
}
return count;
}
为什么不能编译呢?思考一下
问题在于vec是一个const引用,但是我们试图把一个非 const 的iterator绑定在它上面。如果允许这样做,那就没有什么能阻止我们在后面通过iterator修改这个vector的值了。
为了防止这种情况的出现,C++语言要求绑定在onst vector上的iterator也必须是const iterator。我们这样做
// ok: 这次可以通过编译了
vector< type >::const_iterator iter = vec.begin();
const容器只能被绑定在 const iterator 上 这样的要求与 const 指针只能指向 const 数组的 行为一样 在两种情况下 C++语言都努力保证 const 容器的内容不会被改变。
下面我自己模拟一个vector容器,然后实现一个迭代器来操作这个容器
template<typename T>
class Vector;
template<typename T>
class Iterator
{
public:
Iterator(Vector<T>* pv, int idx) :
pvec(pv), index(idx)
{}
bool operator!=(const Iterator left)
{
return index != left.index;
}
T& operator*()const;
const Iterator operator++(int)
{
const Iterator tmp(*this);
index++;
return tmp;
}
const Iterator operator--(int)
{
const Iterator tmp(*this);
index--;
return tmp;
}
const Iterator operator-(int left)
{
return Iterator(pvec, index - left);
}
private:
Vector<T>* pvec;
int index;
};
template<typename T>
class Vector
{
public:
typedef Iterator<T> iterator;
Vector()
{
parr = new T[2]();
cursize = 0;
totalsize = 2;
}
iterator begin()
{
return iterator(this, 0);
}
iterator end()
{
return iterator(this, cursize);
}
void push_back(T data)
{
insert(end(), data);
}
void insert(iterator _where, T data)
{
if (full())
{
resize();
}
for (iterator it = end(); it != _where; it--)
{
*it = *(it - 1);
}
*_where = data;
cursize++;
}
~Vector()
{
delete[] parr;
parr = NULL;
}
void resize()
{
T* pnewspace = new T[totalsize * 2];
memcpy(pnewspace, parr, sizeof(T)*totalsize);
delete[] parr;
parr = pnewspace;
totalsize *= 2;
}
//arr[0] = 10;
T& operator[](int index)
{
return parr[index];
}
private:
bool full()
{
return cursize == totalsize;
}
T* parr;
int cursize;//当前元素个数
int totalsize;//总大小
};
template<typename T>
T& Iterator<T>::operator*()const
{
return (*pvec)[index]; //vec[0];
}
测试一下.......
#include<vector>
int main()
{
Vector<int> vec;
for (int i = 0; i < 10; i++)
{
vec.push_back(i + 1);
}
Vector<int>::iterator it = vec.begin();
while (it != vec.end())
{
std::cout << *it << " ";
it++;
}
std::cout << std::endl;
return 0;
}
运行结果!!
最后说一下STL迭代器删除元素的问题
这个主要考察的是迭代器失效的问题。
1.对于序列容器vector,deque来说,使用erase(iterator)后,后边的每个元素的迭代器都会失效,但是后边每个元素都会往前移动一个位置,但是erase会返回下一个有效的迭代器。
2.对于关联容器map,set来说,使用了erase(iterator)后,当前元素的迭代器失效,但是其结构是红黑树,删除当前元素不会影响到下一个元素的迭代器,所以在调用erase之前,记录下一个元素的迭代器即可。
3.对于list来说,它使用了不连续分配的内存,并且它的erase方法也会返回下一个有效的iterator,因此上面两种正确的方法都可以使用。