C++ 迭代器的類型
C++ 對於常用的數據結構進行了封裝,例如:
- 數組 :
vector
,array
,string
. - 鏈表 :
list
,forward_list
. - 棧 :
stack
. - 隊列 :
deque
,queue
. - 樹 :
set
,map
,multimap
,multiset
- 哈希表:
unorder_set
,unorder_map
,unorder_multiset
,unorder_multimap
.
數據結構的封裝常常是爲了給使用者提供更加方便的操作;上面的數據結構以及非常的多了,如果我們需要操作其中保存在數據結構中的成員應該怎麼辦呢?STL爲了給使用者提供方便,讓使用者在不瞭解任何的底層實現的時候,也能使用這個數據結構成員,給每個容器結構設計了一個迭代器iterator
, 使用者通過iterator
遍歷訪問或者操作容器中的數據,而不需要掌握數據結構(例如二叉樹需要中序遍歷等等)。
C++除了提供容器之外,還對容器提供了相關的算法,例如:
void advance(_InIt& _Where, _Diff _Off)
代表迭代器前移多少個。
爲了使得算法能夠更好的使用迭代器,STL對迭代器提供了分類,每種不同類型的迭代器使用不同的算法實現細節來完成。
1. 迭代器類型的定義
我們以vector
中的迭代器爲例:
template<class _Myvec>
class _Vector_iterator
: public _Vector_const_iterator<_Myvec>
{ // iterator for mutable vector
public:
using _Mybase = _Vector_const_iterator<_Myvec>;
using iterator_category = random_access_iterator_tag;
using value_type = typename _Myvec::value_type;
using difference_type = typename _Myvec::difference_type;
using pointer = typename _Myvec::pointer;
using reference = value_type&;
};
在vector
的迭代器,定義了一個迭代器的類型using iterator_category = random_access_iterator_tag;
,代表這個迭代器可以隨機的訪問(+,-, 偏移等操作)。
STL整個的迭代器類型如下:
struct input_iterator_tag
{ // identifying tag for input iterators
};
struct output_iterator_tag
{ // identifying tag for output iterators
};
struct forward_iterator_tag
: input_iterator_tag
{ // identifying tag for forward iterators
};
struct bidirectional_iterator_tag
: forward_iterator_tag
{ // identifying tag for bidirectional iterators
};
struct random_access_iterator_tag
: bidirectional_iterator_tag
{ // identifying tag for random-access iterators
};
從上我們發現迭代器分爲輸入、輸出、正向、雙向、隨機訪問五種。
每種類型的迭代器使用用一個xxxx_iterator_tag
變量類型來定義。
2. 迭代器類型的使用
我們看下C++的迭代器怎麼使用的,這裏使用最簡單的算法advance
爲例:
template<class _InIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag)
{ // increment iterator by offset, input iterators
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");
for (; 0 < _Off; --_Off)
{
++_Where;
}
}
template<class _BidIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag)
{ // increment iterator by offset, bidirectional iterators
for (; 0 < _Off; --_Off)
{
++_Where;
}
// the following warning is triggered if _Diff is unsigned
#pragma warning(suppress: 6294) // Ill-defined for-loop: initial condition does not satisfy test.
// Loop body not executed.
for (; _Off < 0; ++_Off)
{
--_Where;
}
}
template<class _RanIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_RanIt& _Where, _Diff _Off, random_access_iterator_tag)
{ // increment iterator by offset, random-access iterators
_Where += _Off;
}
template<class _InIt,
class _Diff>
_CONSTEXPR17 void advance(_InIt& _Where, _Diff _Off)
{ // increment iterator by offset, arbitrary iterators
// we remove_const_t before _Iter_cat_t for better diagnostics if the user passes an iterator that is const
_Advance1(_Where, _Off, _Iter_cat_t<remove_const_t<_InIt>>());
}
這裏分別對不同類型的迭代器做實現:
void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag)
: 代表正向的迭代器。void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag)
: 雙向的迭代器:void _Advance1(_RanIt& _Where, _Diff _Off, random_access_iterator_tag)
: 隨機訪問的迭代器。
也就是說算法針對不同類型的迭代器,實現了不同的算法細節。
那麼C++怎麼知道一個迭代器屬於那種類型呢?這個就跟_Iter_cat_t<remove_const_t<_InIt>>()
這個操作有關了,這個操作的代碼如下:
template<class _Iter>
using _Iter_cat_t = typename iterator_traits<_Iter>::iterator_category;
也就是說是使用萃取機的iterator_traits<_Iter>::iterator_category;
來提取迭代器的iterator_category
,然後使用iterator_category
來創建一個臨時對象來匹配函數。
3. 總結
STL的算法針對迭代器進行操作,爲了算法的高效,需要對不同的迭代器使用不同的算法實現,因此每個迭代器都定義了一個iterator_category
來表示迭代器的類型。算法在運行的時候,就會根據iterator_category
不同,來選擇具體的算法細節。