在经典的设计模式中,有一种迭代器模式,定义为:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器的主要优点如下:
1.访问一个聚合对象的内容而无须暴露它的内部表示。
2.遍历任务交由迭代器完成,这简化了聚合类。
3.它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
4.增加新的聚合类和迭代器类都很方便,无须修改原有代码。
5.封装性良好,为遍历不同的聚合结构提供一个统一的接口。
使用过STL的童鞋就知道,迭代器是STL使用最多的技术;那么迭代器具体是怎么实现的呢?本文来讨论一下迭代器的原理和相关实现。
List类
首先,我们简单的模拟一个单项链表,这个链表可以往表头插入数据,并且返回表头。
ListItem
首先,我们需要一个ListItem表示每个链表节点,这个声明如下:
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
| template <typename T> class ListItem; template <typename T> std::ostream& operator<<(std::ostream& out, ListItem<T>& d) { out << d.data_; return out; } template <typename T> class ListItem { public: ListItem(const T& t) : data_(t), next_(nullptr) {} ListItem(T&& t) : data_(std::forward<T>(t)), next_(nullptr) {} void setnext(ListItem<T>* n) { next_ = n; } ListItem<T>* next() { return next_; } friend std::ostream& operator<< <T>(std::ostream& out, ListItem& d); private: ListItem<T>* next_; T data_; };
|
首先这里构造函数:
1.支持普通构造。
2.支持移动函数。
3.支持参数完美转发。
4.友元operator<<
,支持数据输出。
List类
这个类实现一个链表,支持简单的插入,并且返回头部节点。
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
| template <typename T> class List { public: List() noexcept : head_(nullptr) {} void push(const T& t) { ListItem<T>* data = new ListItem<T>(t); data->setnext(head_); head_ = data; } void push(T&& t) { ListItem<T>* data = new ListItem<T>(t); data->setnext(head_); head_ = data; } ListItem<T>* front() { return head_; } private: ListItem<T>* head_; };
|
如上,为了演示,这个类实现的很简单,只支持push,和front两个操作。
iterator
使用过STL都知道,iterator主要是用来遍历容器中的数据节点,那么上面这个List,我们的主要功能是能够不用在外部知道List的实现原理,使用iterator来遍历数据。
所以iterator的主要功能有:
1.支持++,遍历元素。
2.支持*,取元素程序。
3.支持->,指针操作。
4.支持==和!=操作,比较iterator是否到了结尾。
所以这个实现可以如下:
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 50
| template <typename T> class ListIter { public: using value_type = T; using reference = T & ; using const_reference = const T&; using pointer = T * ; using const_pointor = const T*; using size_type = size_t; using difference_type = ptrdiff_t; ListIter(pointer p = nullptr) : data_(p) {} bool operator==(const ListIter& rhs) const noexcept { return data_ == rhs.data_; } bool operator!=(const ListIter& rhs) const noexcept { return data_ != rhs.data_; } ListIter& operator++() { data_ = data_->next(); return *this; } ListIter operator++(int) { ListIter tmp = *this; operator++(); return tmp; } reference operator*() { return *data_; } pointer operator->() { return data_; } private: pointer data_; };
|
使用
接下来,我们看一下这个iterator如何使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int main() { List<std::string> l; l.push("hello"); l.push("world"); l.push("abcd"); l.push("efg"); l.push("kmm"); ListIter<ListItem<std::string>> iter(l.front()); ListIter<ListItem<std::string>> end; while(iter != end) { std::cout << *iter << std::endl; iter++; } return 0; }
|
输出:
1 2 3 4 5
| kmm efg abcd world hello
|
总结
迭代器的主要作用是提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。通用STL的迭代器的作用也是如此,我们在使用STL的时候,不用关系vector,map,set,unordered_map的实现底层原理,使用迭代器,我们就可以遍历容器的节点数据了。