STL學習筆記--3、迭代器iterator與traits編程

iterator模式:提供一種方法,依次巡訪某個聚合物(容器)所含的各個元素,而無需暴露該聚合物的內部表達式。


1、迭代器設計思維

STL在於將數據容器和算法分開,彼此獨立,最後再以一帖粘合劑將它們撮合在一起。只要對算法給予不同的迭代器,就可以對不同容器進行相同的操作。

算法find():接受兩個迭代器和一個搜尋目標。

//摘自SGI<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 <vector>
#include <list>
#include <deque>
#include <algorithm>
#include <iostream>
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>
    vector<int>::iterator it1 = find(ivect.begin(), ivect.end(), 4);
    if (it1 == ivect.end())
        cout << "4 not found." << endl;
    else 
        cout << "4 found. " << *it1 << endl;

    //list<int>
    list<int>::iterator it2 = find(ilist.begin(), ilist.end(), 6);
    if (it2 == ilist.end())
        cout << "6 not found." << endl;
    else
        cout << "6 found. " << *it2 << endl;

    //deque<int>
    deque<int>::iterator it3 = find(ideque.begin(), ideque.end(), 8);
    if (it3 == ideque.end())
        cout << "8 not found." << endl;
    else
        cout << "8 found. " << *it3 << endl;

    return 0;
}

2、迭代器是一種智能指針smart pointer

迭代器的行爲類似與指針。

迭代器中最重要的工作就是opertaor*opertaor->的重載。

簡單的list迭代器實現:

template <typename T>
class List
{
public:
    void insert_front(T value);
    void insert_end(T value);
    void display(std::ostream &os = std::cout) const;
    //...
private:
    ListNode<T> *_end;
    ListNode<T> *_front;
    long _size;
};
template<typename T>
class ListItem
{
public:
    T value() const{retrun _value;}
    ListItem* next() const{return _next;}
private:
    T _value;
    ListItem* _next;//單向鏈表
};
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;
    }

    //後置++,提供一個int類型的函數參數,返回類型爲右值
    //用前置++來實現後置版本
    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; }
};

void main()
{
    List<int> mylist;

    //鏈表初始化
    for(int i=0; i<5; i++)
    {
        mylist.insert_front(i);
        mylist.insert_end(i+2);
    }
    mylist.display();//10(4 3 2 1 0 2 3 4 5 6)

    //頭迭代器
    ListIter<ListNode<int>> begin(mylist.get_front());
    //尾迭代器;使用默認構造函數 end=null
    ListIter<ListNode<int>> end;
    //使用默認構造函數;iter=null
    ListIter<ListNode<int>> iter;

    iter = find(begin,end,2);
    if(iter == end)
        cout<<findnum<<" not find"<<endl;
    else
        cout<<findnum<<" find"<<endl;

    return 0;
}

find()函數內,以*iter!=value來判斷元素是否吻合。
本例中value爲int,iter爲ListItem<int>類型,需提供兩者間的operator!=

template <typename T>
bool operator!=(const ListItem<T>& item,T n)
{
    return item.value()!=n;
}

3、迭代器相應型別(associated type)

假設算法中需要聲明一個迭代器指向的對象的類型的變量?
decltype,typeid都做不到。

解決辦法:函數模板的參數推導。函數模板,編譯器自動根據實參類型自動推導。


4、Traits編程

template參數推導機制只能適用於函數參數,若爲變量爲返回值,則無法推導。因爲返回值不在函數模板的作用於內。

解決方法2:聲明內嵌類型。

template<class T>
struct MyIter
{
    typedef T value_type;  // 內嵌value_type類型定義
    T* ptr;
    MyIter(T* p=0) : ptr(p) {}
    T& operator*() const { return *ptr; }
    //....
};

template<class I>
//typename用於指明I::value_type是個類型
//如果沒有typename的話,編譯器將把value_type當成I的一個member或者member function。
typename I::value_type func(I ite)
{
    return *ite;
}

int main(int argc, char **argv)
{
    MyIter<int> ite(new int(8));
    //調用func(),返回值類型爲value_type
    cout << func(ite) << endl;
    return 0;
}

但並非所有的迭代器都是class type。對於原生指針,需要進行模板偏特化(Partial Specialization)。

C++模板的分兩種:偏特化和全特化(所謂偏特化是指部分特化)。
1、特化(或者說全特化,specialized)不過是一個花哨的術語,意思是形參不再爲形參,它們已經有了確定的值;
類模板可以部分特化;函數模板所有模板參數均需要提供默認實參。
2、偏特化(partial specialization)的意思是提供另一份template的定義,其本身仍然是一個template,或者說針對template參數更進一步的條件限制所設計出來的一個特化版本。

//“萃取”迭代器特性
//一般版本
template<class I>
struct iterator_traits{
    typedef typename I::value_type value_type;
};

//偏特化<T*>
template<class T>
struct iterator_traits<T*>{
    typedef T value_type;
};

//偏特化<const T*>
template<class T>
struct iterator_traits<const T*>{
    typedef T value_type;
};
//func()函數現在可以改寫爲
template<class I>
typename iterator_traits<I>::value_type func(I iter)
{
    return *iter;
}

最常用到的迭代器的五種類型:

1、value type 用來表示迭代器所指對象的型別;
2、difference type 用來表示兩個迭代器之間的距離;
3、reference 爲引用類型;
4、pointer 爲指針類型;
5、iterator_category 表明迭代器的類型;

template<class T>
struct iterator_traits{
    typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type value_type;
    typedef typename I::difference_type difference_type;
    //需對pointer設計const版本的偏特化
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};

1、value type

用來表示迭代器所指對象的型別;

2、difference type

用來表示兩個迭代器之間的距離;頭尾之間的距離爲容器的最大容量。

STL中count()返回值類型就是difference_type;

template<class T,class I>
typename iterator_traits<I>::difference_type count(I first,I last,const T& value)
{
    typename iterator_traits<I>::difference_type n=0;
    for(;first!=last;++first)
    {
        if(*first==value)
            ++n;
    }
    return n;
}

針對原生指針,C++內建ptrdiff_t類型。

//<T*>
template<class T>
struct iterator_traits<T*>{
typedef ptrdiff_t difference_type;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef ptrdiff_t difference_type;
};

3、reference type

4、pointer type

//<T*>
template<class T>
struct iterator_traits<T*>{
typedef T* pointer;
typedef T& reference;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef const T* pointer;
typedef const T& reference;
};

5、iterator_category 迭代器的類別

迭代器被分爲五類:
1、Input Iterator:這種迭代器所指對象,不允許外界改變,只讀(read only);
2、Output Iterator:唯寫(write only);
3Forward Iterator:允許「寫入型」算法(例如 replace())在此種迭代器所形成的區間上做讀寫動作;
4、Bidirectional Iterator:可雙向移動。某些算法需要逆向走訪某個迭代器區間(例如逆向拷貝某範圍內的元素),就可以使用 Bidirectional Iterators;
5、Random Access Iterator:前四種迭代器都只供應一部份指標算術能力(前3種支持 operator++ ,第4種再加上 operator--),第5種則涵蓋所有指標算術能力,包括 p+n, p-n, p[n], p1-p2, p1<p2.

advance()

以advance()函數爲例:函數有兩個參數,迭代器p和數值n;函數內部將p前進或後退n步,針對不同的迭代器實現的方式不同。

//定義5個class,代表5種迭代器的類型
//五種迭代器tag的定義
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
template <class InputIterator, class Distance>
inline void __advance(InputIterator &i,Distance n,input_iterator_tag)
{
    //單向,逐一
    while(n--) ++i;
}
template <class ForwardIterator, class Distance>
inline void __advance(ForwardIterator &i,Distance n,forward_iterator_tag)
{
    __advance(i,n,input_iterator_tag());
}
template <class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator &i,Distance n,bidirectional_iterator_tag)
{
    //雙向,逐一
    if(n>=0)
        while(n--) ++i;
    else
        while(n++) --i;
}
template <class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator &i,Distance n,random_access_iterator_tag)
{
    //雙向,跳躍
    i+=n;
}
template <class InputIterator, class Distance>
inline void advance(InputIterator &i,Distance n)
{
    __advance(i,n,iterator_traits<InputIterator>::iterator_category());
}
//針對原生指針,迭代器類型爲RandomAccessIterator
//<T*>
template<class T>
struct iterator_traits<T*>{
typedef random_access_iterator_tag iterator_category;
};
//<const T*>
template<class T>
struct iterator_traits<const T*>{
typedef random_access_iterator_tag iterator_category;
};

任何迭代器,類型總是該迭代器所隸屬的類型中最強化的那一個
STL命名規則:以算法所能接受的最低階迭代器類型爲迭代器參數命名。

distance()

//InputIterator版本的實現。
//因爲迭代器之間的繼承原因,ForwardIterator,BidirectionalIterator也是同樣調用這個版本實現。
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type __distance(InputIterator first,InputIterator last,input_iterator_tag)
{
    iterator_traits<InputIterator>::difference_type n=0;
    //逐一
    while(first!=last)
    {
        ++first;
        ++n;
    }
    return n;
}

//RandomAccessIterator版本的實現
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type __distance(RandomAccessIterator first,RandomAccessIterator last,random_access_iterator_tag)
{
    iterator_traits<RandomAccessIterator>::difference_type n=0;
    //跳躍
    //直接計算
    return last-first;
}


//頂層封裝distance()兩個參數
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type distance(InputIterator first,InputIterator last)
{
    typedef typename iterator_traits<InputIterator>::iterator_category() category;
    return __distance(first,last,category);
}

5、iterator類

STL提供的iterator class如下。它不含任何成員,純粹只是型別定義。其中後三個參數提供了默認值,則在定義新的迭代器時只需提供前兩個參數即可。

template <class Category,
          class T,
          class Distance = ptrdiff_t,
          class Pointer = T*,
          class Reference = T&>
struct iterator {
    typedef Category    iterator_category;
    typedef T           value_type;
    typedef Distance    difference_type;
    typedef Pointer     pointer;
    typedef Reference   reference;
};
//ListIter
std::iterator<std::forword_iterator_tag,Item>

6、SGI STL之__type_traits

  • iterator_traits負責萃取迭代器的特性;
  • __type_traits則負責萃取類型的特性。

根據定義與SGI<type_traits.h>中的__type_traits,針對不同的類型屬性,在編譯時期完成對不同實現函數的派送決定。

//__type_traits<T>:T可以是任意類型
__type_traits<T>::has_trivial_default_constructor;  
__type_traits<T>::has_trivial_copy_constructor;  
__type_traits<T>::has_trivial_assignment_operator;  
__type_traits<T>::has_trivial_destructor;  
__type_traits<T>::is_POD_type; 

上述5個式子,值只有兩種值__true_type or __false_type
根據其值來選擇對類型進行快速的拷貝賦值或是安全版本方式

C++針對標量型定義特化版本。每個成員的值都是__ture_type

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章