0%

浅析iterator

引言

在日常开发学习中,熟练的使用设计模式可以极大的提高我们的工作效率。诚然,对于那么多的设计模式,我们往往仅会去深入了解日常被使用最多的设计模式。
当然,也有一些设计模式,随着高级语言的不断扩展,逐渐走向了没落。一如今天所要讨论的迭代器模式,由于容器的大量使用,使得iterator这一设计模式使用度就降低了许多。那我们就具体聊聊关于迭代器的那些事吧!

什么是迭代器模式

设计模式中对迭代器的定义是: Iterator是用来解决对聚合对象的遍历问题,将聚合的遍历封装到一个类中进行,这样就避免了暴露这个聚合对象的内部表示的可能
很官方,但是也是相对的晦涩一点,让我们先看看它的经典UML图,再来开始讨论它:
avatar

如前面所述,迭代器主要是去用来遍历一个聚合元素,这个聚合元素就是以某种顺序方式存储了数据。它可以是一个整数数组,以顺序存储的方式保存数据,抑或是其它的简单数据结构。
我们可以通过简单的指针操作得到这个迭代的结果的,但是对于一些复杂数据结构迭代处理起来就会比较麻烦。依定义所说,这会导致对象内部数据表示的暴露。我想读到这里大家一定有如下一些共性问题:
1. 如何我隐藏内部数据表示?
2. 我们是否不管在任何情况下,都需要实现一个迭代器来辅助我们去遍历吗?
3. 如果不是,那又何时才是实现它的好时机呢?
让我们一个一个的来处理这些问题。

首先,我们可以思考如下的一个例子: 程序中有一个对象数组需要被遍历,通常情况下,我想大家都会使用如下的方法:

1
2
for (int i = 0; i < n; ++i) {...}
while (i < n) {...}

对于一个数组来说使用如上的方法来说,可以是十分的简单。但若是一个较为复杂的数据结构如: 链表、树、图等,对于它们的迭代遍历,我们可远非如同遍历数组一样的容易了。每次的迭代程序,可能都需要一定的测试保证其正确性后才会投入使用。但若是使用迭代器来说就相对的轻松许多,你无需关心它是如何实现的,在使用上所有的迭代器都是一致的。

实例分析

我们可以使用迭代器完成如下vector容器的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>
#include <iterator>

using std::vector;
using std::cout;
using std::iterator;

int main() {
vector<int> res = {1,2,3,4,5,6,8};
vector<int>::iterator myiterator = res.begin();
for (; myiterator != res.end(); ++myiterator) {
cout << *myiterator << "\t";
}
}

如上所示,十分的容易。除此之外,你还可以将vevtor替换为set、map等数据结构,同样的操作依旧是完全适用的。
这样的表示,将迭代操作交由外部类处理,无需内部提供一个迭代的操作,降低了暴露内部实现的细节。

使用场合

并非所有的情况下,都需要使用迭代器的。毕竟这会增加系统的一个复杂度,因此它的应用场景大致如下所示:

  • 访问一个聚合对象的内容,不需要暴露它的内部表示
  • 支持聚合对象的多种遍历
  • 迭代器模式与集合同时存在

既然已经了解它的设计原理,那就动手实践一下吧!

设计一个迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* 迭代器的主要功能
* 1. 支持++, 遍历元素
* 2. 支持* , 取元素
* 3. 支持->, 指针操作
* 4. 支持==与!=, 判断是否到达迭代器尾部
*/

namespace Test {
template <class T>
class iterator {
public:
using value_type = T;
using reference = T& ;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using size_type = size_t;
using difference_type = ptrdiff_t;

iterator(pointer p = nullptr) : arr(p) {}

bool operator==(const Array& rhs) const {
return arr == rhs.arr;
}

bool operator!= (const Array& rhs) const {
return arr != rhs.arr;
}

Array& operator++() {
arr = arr->next();
return *this;
}
Array& operator++(int) {
value_type tmp = *this;
++&*this;
return tmp;
}
reference operator*() {
return *arr;
}
reference operator->() {
return arr;
}
private:
pointer arr;
}
}

总结

迭代器就是对于指针的某些功能的封装,在一般的开发过程中,基本不会需要去实现这样一个迭代器。但是这其中的原理,我们需要去理解,将这些设计的想法应用到我们日常的开发之中。