1. 程式人生 > 其它 >stl原始碼剖析01——迭代器

stl原始碼剖析01——迭代器

技術標籤:stl原始碼剖析stl

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容器都提供了一份專屬於自己的迭代器的緣故