迭代器模式简介
先介绍一下迭代器模式的含义,迭代器模式Iterator的核心功能,就是提供了一种特定的方法,顺序访问一个容器中的各个元素,既不会暴露容器的内部设计细节(容器底层数据结构),又可以让外部代码透明的访问集合内部的所有元素。
先给出迭代器模式的一个UML类图设计如下:
所以一个迭代器最基本应该提供下面这几个接口操作,代码如下:
#include<iostream>
using namespace std;
//迭代器的抽象基类
template<typename _Ty>
class Iterator
{
public:
//是否还有下一个元素
virtual bool hasNext() = 0;
//返回下一个元素
virtual _Ty& next() = 0;
};
上面定义了一个迭代器的抽象基类
hasNext接口表示聚合对象(也就是容器)内部是否还有下一个元素,next接口返回下一个元素的值引用,通过这样的一个迭代器实例遍历访问容器的内部元素,不需要知道容器内部的具体实现,对于用户来说,完全是透明的。
当然不同的容器,由于其内部数据结构不同,迭代器遍历它们的方式也就不同了,因此迭代器一般设计为容器的嵌套类,直接在容器内部实现迭代器迭代的逻辑过程,请看下面的代码示例。
迭代器模式代码示例
看如下代码示例:
#include<iostream>
using namespace std;
//迭代器的抽象基类
template<typename _Ty>
class Iterator
{
public:
//是否还有下一个元素
virtual bool hasNext() = 0;
//返回下一个元素
virtual _Ty& next() = 0;
};
//容器类的定义
template<typename _Ty>
class CArray
{
public:
//只提供了容器添加元素的操作
template<typename _Ty>
void push(_Ty&& val)
{
_vec.push_back(std::forward<_Ty>(val));
}
//当前容器迭代器的实现
class ConcreteIterator : public Iterator<_Ty>
{
public:
//迭代器的构造函数
ConcreteIterator(CArray<_Ty>& arr)
:_array(arr)
{
_cur = 0;
}
//判断是否还有下一个元素
bool hasNext()
{
int size = _array._vec.size();
return _cur < size;
}
//返回下一个元素的值
_Ty& next()
{
_Ty& val = _array._vec[_cur];
_cur++;
return val;
}
private:
CArray<_Ty>& _array;//获取当前迭代器所迭代器的容器的引用
int _cur;//当前迭代器的元素下标
};
//返回容器的迭代器的函数接口
Iterator<_Ty>* createIterator()
{
return new ConcreteIterator(*this);
}
private:
vector<_Ty> _vec;//存储元素的容器
};
CArray在这里作为一个容器的实现,它底层直接使用vector容器作为元素的存储结构,其内部实现了一个迭代器ConcreteIterator,从Iterator继承而来,重写了hasNext和next两个方法,作为具体的遍历CArray容器的迭代器,又提供了一个createIterator函数,专门返回该容器对象的迭代器,具体遍历的代码如下:
int main()
{
//定义容器对象
CArray<int> array;
//添加右值参数
array.push(20);
array.push(56);
array.push(3);
array.push(21);
//添加左值参数
int data = 89;
array.push(89);
//用迭代器遍历访问容器对象内部的元素并打印
Iterator<int>* it = array.createIterator();
while (it->hasNext())
{
int val = it->next();
cout << val << " ";
}
cout << endl;
return 0;
}
上面main函数中,迭代器遍历访问容器的代码就是通用的代码,不论容器底层数据结构怎么变化,迭代器遍历容器的方式不会改变,其底层差异细节完全封装在了迭代器的hasNext和next函数中,对于用户来说内部操作完全是透明的。
熟悉C++ STL容器内部迭代器设计的人都知道,STL容器迭代器的设计和上面通用迭代器模式本质相同,实现细节上还是有一些区别,下面做一下对比。
C++ STL的容器迭代器设计
C++ STL内部的容器迭代器和上面通用迭代器模式的设计类比:
1、迭代器设计成容器的嵌套类型,因为不同的容器由于其底层数据结构的不同,需要为每个容器实现自己的迭代器进行具体的数据遍历。
2、C++STL容器统一提供begin()和end()方法返回容器起始元素的迭代器和末尾元素后继位置的迭代器,类比上面CArray容器提供的createIterator方法。
3、C++STL的迭代器通过和容器的end() 相比较来判断容器元素是否迭代完成,类比上面通用迭代器的hasNext方法。
4、C++STL的迭代器把容器内部数据遍历的细节封装在了迭代器的operator++运算符重载函数里面,并提供迭代器的operator*()运算符重载函数,访问迭代器遍历的元素的值,类比上面通用迭代器的next方法的功能。
下面演示一个C++STL容器迭代器的标准使用方式,代码示例如下:
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
int main()
{
//定义vector容器对象,添加20个随机整数
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 20; ++i)
{
vec.push_back(rand() % 100 + 1);
}
//通过vector容器的迭代器遍历访问容器的所有元素
vector<int>::iterator it = vec.begin();
for (; it != vec.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//C++11提供了新的容器遍历语法foreach,其底层就是通过迭代器来实现的
for (int val : vec)
{
cout << val << " ";
}
cout << endl;
return 0;
}