stl原始碼剖析01——迭代器
阿新 • • 發佈:2021-02-16
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容器都提供了一份專屬於自己的迭代器的緣故