1. 程式人生 > >STL-迭代器原始碼剖析

STL-迭代器原始碼剖析

         在接觸了STL後,覺得STL就是一個為效率而生的怪物,而且令人驚訝的是它裡面的很多演算法和模板對於學C++的人來說是非常有價值去剖析的。

       迭代器(Iterator)模式--提供一種方法,使之能夠依序尋訪某個聚合物(容器)所含的各個元素,而又無需暴露該聚合物的內部表達方式。
STL的中心思想在於:將資料容器和演算法分開,彼此獨立設計,最後再以一貼膠合劑(iterator)將它們撮合在一起。

迭代器是資料結構容器中非常有用的“特殊指標”,在不暴露容器中結構的條件下,可以取出容器裡所有的元素。

<span style="font-family:Microsoft YaHei;font-size:14px;">vector<int > v1;
v1.push_back (5);
v1.push_back (4);
v1.push_back (3);
v1.push_back (2);
v1.push_back (1);
// 迭代器遍歷順序表
vector<int >::iterator it = v1 .begin();
for (; it != v1. end(); ++it )
{
cout<<*it <<" ";
}
cout<<endl ;</span>

相信很多人在看STL原始碼的時候會有很多的疑問,確實STL是大神們寫的程式碼。我們很難去看懂。裡面的巨集定義和輔助函式我們可以忽略,只把整個框架提取出來,再往框架中不斷的新增功能,最後就會成為STL原始碼。所以我們現在的任務就是提取Iterator的框架。

一:Iterator的呼叫介面:

<span style="font-family:Microsoft YaHei;font-size:14px;">//通過Distance來計算兩個迭代器之間的距離
//通過Category來確定是前向迭代器還是雙向迭代器,還是隨機迭代器
template<class T, class Distance, class Category,
class Pointer = T*, class Reference = T&>
</span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;">struct Iterator
{
	typedef Category CategoryIterator;//迭代器型別
	typedef T ValueType;				//迭代器所指物件的型別
	typedef Distance DifferenceType;	//兩個迭代器之間的距離
	typedef Pointer Pointer;			//迭代器所指型別的指標
	typedef Reference Reference;		//迭代器所指型別的引用
};</span>


      這是Iterator的入口,我們可以看到class Iterator中其實沒有很多的東西,就是有5個typedef。利用模板傳進來的引數CategoryIterator可以提取出迭代器的型別:

二:迭代器的型別:

<span style="font-family:Microsoft YaHei;font-size:14px;">struct InputIteratorTag{};
struct OutputIteratorTag{};
struct ForwardIteratorTag :<span style="color:#ff0000;">public </span>InputIteratorTag{};
struct BidirectionalIteratorTag :<span style="color:#ff0000;">public</span> InputIteratorTag{};
struct RandomAccessIteratorTag :<span style="color:#ff0000;">public </span>InputIteratorTag{};


//Distance是迭代器之間的距離
template<class T,class Distance>
struct InputIterator         //只讀迭代器
{
	typedef InputIteratorTag CategoryIterator;
	typedef T ValueType;
	typedef Distance DifferenceType;
	typedef T* Pointer;
	typedef T& Reference;
};
</span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">template<class T, class Distance>
struct OutputIterator        //只寫迭代器
{
	typedef OutputIteratorTag CategoryIterator;
	typedef T ValueType;
	typedef Distance DifferenceType;
	typedef T* Pointer;
	typedef T& Reference;
};</span>

template<class T, class Distance>struct ForwardIterator //前向迭代器{typedef ForwardIteratorTag CategoryIterator;typedef T ValueType;typedef Distance DifferenceType;typedef T* Pointer;typedef T& Reference;};template<class T, class Distance>struct BidirectionalIterator //雙向迭代器{typedef BidirectionalIteratorTag CategoryIterator;typedef T ValueType;typedef Distance DifferenceType;typedef T* Pointer;typedef T& Reference;};template<class T, class Distance>struct RandomAccessIterator //隨機迭代器{typedef RandomAccessIteratorTag CategoryIterator;typedef T ValueType;typedef Distance DifferenceType;typedef T* Pointer;typedef T& Reference;};

    這5個迭代器中的繼承關係使雙向迭代器的物件可以傳給前向迭代器,隨機迭代器的物件可以傳給前向迭代器和雙向迭代器,它的原理就是“切片規則”。這就是為什麼隨機迭代器可以隨機機訪問迭代器的內容。

三:迭代器萃取機

        STL中有那麼多的迭代器,它是通過什麼保證程式可以正確的呼叫合適的迭代器呢?

<span style="font-family:Microsoft YaHei;font-size:14px;">template<class Iterator>
struct IteratorTraits       //Iterator類有內嵌型別
{
	typedef typename Iterator::CategoryIterator  IteratorCategory;
	typedef typename Iterator::ValueType ValueType;
	typedef typename Iterator::DifferenceType DifferenceType;
	typedef typename Iterator::Pointer Pointer;
	typedef typename Iterator::Reference Reference;
};</span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">//偏特化原生的型別,為了原生型別而存在的特化
template<class T>
struct IteratorTraits<T*>
{
	typedef RandomAccessIteratorTag IteratorCategory;
	typedef T ValueType;
	typedef ptrdiff_t DifferenceType;
	typedef T* Pointer;
	typedef T& Reference;
};


//如上
template<class T>
struct IteratorTraits<const T*>
{
	typedef RandomAccessIteratorTag IteratorCategory;
	typedef T ValueType;
	typedef ptrdiff_t DifferenceType;
	typedef const T* Pointer;
	typedef const T& Reference;
};
</span>
        typename關鍵字告訴編譯器Iterator是一個型別,例項化出物件的時候在來編譯。這是class關鍵字不具有的功能。這裡有三種類型萃取機,他們分別代表三種不同的型別。

                  內建型別,原生指標型別,const原生指標型別。

<span style="font-family:Microsoft YaHei;font-size:14px;">template <class InputIterator>
inline typename IteratorTraits <InputIterator>::DifferenceType
Distance(InputIterator first, InputIterator last)
{
	//通過傳入的InputIterator決定是哪個型別的IteratorCategory物件
	return _Distance(first, last, IteratorTraits <InputIterator>::IteratorCategory());
}


//前向和雙向迭代器的Distance函式,都是通過遍歷兩個迭代器之間的元素實現的
template <class InputIterator>
inline typename IteratorTraits <InputIterator>::DifferenceType
_Distance(InputIterator first, InputIterator last, InputIteratorTag)
{
	IteratorTraits<InputIterator >::DifferenceType n = 0;
	while (first != last) {
		++first; ++n;
	}
	return n;
}

//隨機迭代器的distance
template <class RandomAccessIterator>
inline typename IteratorTraits <RandomAccessIterator>::DifferenceType
_Distance(RandomAccessIterator first, RandomAccessIterator last,RandomAccessIteratorTag)
{
	return last - first;
}</span>



<span style="font-family:Microsoft YaHei;">template <class InputIterator, class Distance>
inline void Advance(InputIterator & i, Distance n)
{
	_Advance(i, n, IteratorTraits <InputIterator>::IteratorCategory());
}
template <class InputIterator, class Distance>
inline void _Advance(InputIterator & i, Distance n, InputIteratorTag)
{
	while (n--) ++i;
} 
template <class BidirectionalIterator, class Distance>
inline void _Advance(BidirectionalIterator & i, Distance n,
BidirectionalIteratorTag)
{
	if (n >= 0)
		while (n--) ++i;
	else
		while (n++) --i;
} 
template <class RandomAccessIterator, class Distance>
inline void __Advance(RandomAccessIterator & i, Distance n,
RandomAccessIteratorTag)
{
	i += n;
} </span>
       advance的提取方法和distance相同。

所以我們可以看到,模板是為STL而生的一點都不為過。通過三個萃取機IteratorTraits可以將原生的內建型別自定義的型別分開是萃取機的精華所在。