1. 迭代器概述
- 不论是泛型思想或是STL的实际运用,迭代器都扮演着重要的角色
- STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一种胶着剂将它们撮合在一起
2. 迭代器是独立于容器和算法而存在的
下面介绍一个容器、算法、迭代器在一起使用的案例
演示案例
- 下面是find()算法的源码(摘录于<stl_algo.h>)
template<class InputIterator,class T> InputIterator find(InputIterator first, InputIterator last, const T& value) { while (first != last&&*first != value) ++first; return first; }
- 在程序中定义了不同的迭代器,find()能够根据不同的容器进行查找操作:
#include <iostream> #include <algorithm> #include <vector> #include <list> #include <deque> using namespace std; int main() { const int arraySize = 7; int ia[arraySize] = { 0,1,2,3,4,5,6 }; vector<int> ivect(ia, ia + arraySize); list<int> ilist(ia, ia + arraySize); deque<int> ideque(ia, ia + arraySize); vector<int>::iterator iter1 = find(ivect.begin(), ivect.end(), 4); if (iter1 == ivect.end()) std::cout << "4 not found" << std::endl; else std::cout << "4 found" << std::endl; list<int>::iterator iter2 = find(ilist.begin(), ilist.end(), 6); if (iter2 == ilist.end()) std::cout << "6 not found" << std::endl; else std::cout << "6 found" << std::endl; deque<int>::iterator iter3 = find(ideque.begin(), ideque.end(), 8); if (iter3 == ideque.end()) std::cout << "8 not found" << std::endl; else std::cout << "8 found" << std::endl; system("pause"); return 0; }
- 可以看出,迭代器是独立于容器与算法的
三、迭代器是一种smart pointer(智能指针)
- 迭代器是一种行为类似指针的对象,而指针的各种行为中最常见的便是“内容提领”与“成员访问”。因此迭代器最重要的工作就是对operator*和operator->进行重载
以auto_ptr为例
- auto_ptr现在已过时,取代物是unique_ptr。但是此处我们以auto_ptr来演示说明
- auto_ptr是一个用来包装原生指针的对象
- 下面是auto_ptr的简略化的源码:
template <class T> class auto_ptr { public: explicit auto_ptr(T* p = 0) :pointee(p) {} template<class U> auto_ptr(auto_ptr<U>& rhs) : pointee(rhs.release()) {} ~auto_ptr() { delete pointee; } template<class U> auto_ptr<T>& operator=(auto_ptr<U>& rhs) { if (this != rhs) reset(rhs.release()); return *this; } T& operator*()const { return *pointee; } T* operator->()const { return pointee; } T* get()const { return pointee; } private: T* pointee; };
- 下面是auto_ptr的使用:
int main() { auto_ptr<string> ps(new string("dongshao")); std::cout << *ps << std::endl; //输出dongshao std::cout << ps->size() << std::endl;//输出5 return 0; }
设计自己的迭代器
- 我们有一个自己的链表容器类List,其中ListItem是链表的节点类
template<typename T> class List { public: void insert_front(T value); void insert_end(T value); void display(std::ostream &os = std::cout)const; ListItem* end()const { return _end; } ListItem* front()const { return _front; } private: ListItem<T>* _end; ListItem<T>* _front; long _size; }; template<typename T> class ListItem { public: T value()const { reutrn value; } ListItem* next()const { return return _next; } private: T _value; ListItem* _next; };
- 如何将我们的List应用于我们的find()函数呢?我们需要为它设计一个行为类似指针的外衣,也就是一个迭代器:
- 我们提领(*)这个迭代器时,返回的是节点对象ListItem
- 当我们递增该迭代器时,指向于下一个ListItem对象
- 但是为了让迭代器适用于任何类型的节点,而不仅限于ListItem,我们将迭代器设计为一个类模板
template<class Item> struct ListIter { Item* ptr; ListIter(Item* p = 0) :ptr(p) {} Item& operator*()const { return *ptr; } Item* operator->()const { return ptr; } ListIter& operator++() { ptr = ptr->next(); return *this; } ListIter& operator++(int) { ListIter tmp = *this; ++*this; return tmp; } bool operator==(const ListIter& i)const { return ptr == i.ptr; } bool operator!=(const ListIter& i)const { return ptr != i.ptr; } };
- 由于find()算法内以*iter!=value来检查元素值是否吻合,而本例中的value的类型是int,iter的类型是ListItem<int>,两者不能直接进行比较,因此还需要设计一个全局的operator!=重载函数,并以int和ListItem<int>为参数:
template<typename T> bool operator!=(const ListItem<T>& item, T n) { return (item->value() != n); }
- 现在我们可以使用我们自己的容器与迭代器了:
int main() { List<int> myList; for (int i = 0; i < 5; ++i) { myList.insert_front(i); myList.insert_end(i + 2); } ListIter<ListItem<int> > begin(myList.front()); ListIter<ListItem<int> > end; ListIter<ListItem<int> > iter; iter = find(begin, end, 3); if (iter == end) std::cout << "3 not found" << std::endl; else std::cout << "3 found" << std::endl; return 0; }
- 从上面的实现可以看出,为了完成一个针对List而设计的迭代器,我们暴露了太多List实现细节:
- 在main函数中,为了制作begin()和end()两个迭代器,我们暴露了ListItem
- 在ListIter类中为了达成operator++的目的,我们暴露了ListItem的操作函数next()
- 如果不是为了迭代器的设计,ListItem原本应该完全隐藏。所以,为了避免暴露我们设计容器的细节,我们应该将迭代器的所有实现细节也封装其容器类中,这也正是每一个STL容器都提供了一份专属于自己的迭代器的缘故