C++ 實(shí)現(xiàn)自定義類型的迭代器操作
##動(dòng)機(jī)
我們知道STL實(shí)現(xiàn)了很多算法(#include<algorithm>),如果項(xiàng)目是基于STL構(gòu)建那么能夠最大化使用現(xiàn)有代碼當(dāng)然是最好的。在STL中容器和算法之間的橋梁是迭代器。所以在定義好自定義類型的容器后,接下來就是迭代器的實(shí)現(xiàn)。
STL中的迭代器
迭代器模式是一種經(jīng)典的設(shè)計(jì)模式,而STL的迭代器實(shí)現(xiàn)用到了模板的一些特性和技能,在這里稍微介紹一下
下面是STL中結(jié)構(gòu)體iterator的定義,這么定義是給后面的算法多態(tài)和萃取時(shí)(具體見書中介紹)使用的。
其中的_Category 和_Ty 沒有默認(rèn)值,需要自己給參數(shù)的。
_Ty就是元素的類型
template<class _Category,
class _Ty,
class _Diff = ptrdiff_t,
class _Pointer = _Ty *,
class _Reference = _Ty&>
struct iterator
{ // base type for iterator classes
typedef _Category iterator_category;
typedef _Ty value_type;
typedef _Diff difference_type;
typedef _Diff distance_type; // retained
typedef _Pointer pointer;
typedef _Reference reference;
};
而_Category是迭代器的類型,主要有以下幾種
// ITERATOR STUFF (from <iterator>)
// ITERATOR TAGS (from <iterator>)
struct input_iterator_tag //只讀
{ // identifying tag for input iterators
};
struct _Mutable_iterator_tag //只寫
{ // identifying tag for mutable iterators
};
struct output_iterator_tag //只寫
: _Mutable_iterator_tag
{ // identifying tag for output iterators
};
struct forward_iterator_tag //前向移動(dòng)
: input_iterator_tag, _Mutable_iterator_tag
{ // identifying tag for forward iterators
};
struct bidirectional_iterator_tag //可雙向移動(dòng)
: forward_iterator_tag
{ // identifying tag for bidirectional iterators
};
struct random_access_iterator_tag //隨機(jī)讀寫
: bidirectional_iterator_tag
{ // identifying tag for random-access iterators
};
//...
自定義迭代器
我希望迭代器有以下操作:*,++。另外還想要通過迭代器調(diào)用count_if函數(shù)。那看一下count_if都用到哪些操作符吧
// TEMPLATE FUNCTION count_if
template<class _InIt,
class _Pr> inline
typename iterator_traits<_InIt>::difference_type
_Count_if(_InIt _First, _InIt _Last, _Pr _Pred)
{ // count elements satisfying _Pred
typename iterator_traits<_InIt>::difference_type _Count = 0;
for (; _First != _Last; ++_First)
if (_Pred(*_First))
++_Count;
return (_Count);
}
可以看到用到了++,!=,*。所以我們的迭代器需要把這些都給實(shí)現(xiàn)了。代碼很簡單:
#include<iterator>
template<class T>
class MyIterator : public iterator<input_iterator_tag, T>{
public:
MyIterator(T* p){
_ptr = p;
}
//賦值
MyIterator& operator = (const MyIterator &iter)
{
_ptr = iter._ptr;
}
//不等于
bool operator != (const MyIterator &iter)
{
return _ptr!= iter._ptr;
}
//等于
bool operator == (const MyIterator &iter)
{
return _ptr == iter._ptr;
}
//前綴自加
MyIterator& operator ++ ()
{
_ptr++;
return *this;
}
//后綴自加
MyIterator operator ++ (int)
{
MyIterator tmp= *this;
_ptr++;
return tmp;
}
//取值
T& operator * ()
{
return *_ptr;
}
private:
T* _ptr;//實(shí)際的內(nèi)容指針,通過該指針跟容器連接
};
自定義容器
下面給出個(gè)簡單的數(shù)組容器,實(shí)現(xiàn)了數(shù)組的基本操作。并把剛剛定義的迭代器內(nèi)置了
template<class T>
class myVector{
public:
typedef MyIterator<T> iterator;//所有類型迭代器用同一個(gè)名字,便于寫出更通用的代碼
myVector(){
_selfElems = new T[32];
_count = 32;
init();
}
myVector(int n){
_selfElems = new T[n];
_count = n;
init();
}
void init(){
memset(_selfElems, 0, sizeof(T)* _count);
}
//常用接口
T& operator[](int i){
return _selfElems[i];
}
iterator begin(){
return iterator(_selfElems);
}
iterator end(){
return iterator(_selfElems + _count);
}
int size() const {
return _count;
}
private:
T* _selfElems;
int _count;
};
##測試
定義一個(gè)vector和自定容器myVector,用迭代器去訪問,并通過迭代器使用conunt_if函數(shù),可以看到用法完全一樣
bool eq_10(int k){
return k == 10;
}
int main(){
//自定義類型
myVector<int> mv(10);
mv[3] = 10; mv[9] = 10;
myVector<int>::iterator it = mv.begin();
cout <<"mv:"<<endl;
while (it != mv.end()){
cout << *(it++) << " ";
}
cout << endl;
cout << count_if(mv.begin(), mv.end(), eq_10) << endl;
//STL 容器
vector<int> v(10,0);
v[3] = 10; v[9] = 10;
vector<int>::iterator it1 = v.begin();
cout << "v:" << endl;
while (it1 != v.end()){
cout << *(it1++) << " ";
}
cout << endl;
cout << count_if(mv.begin(), mv.end(), eq_10) << endl;
getchar();
return 0;
總結(jié)和思考
所以簡單來說,如果想要定義自己容器的迭代器并想通過迭代器調(diào)用STL的算法函數(shù)的話。首先繼承iteroter,然后實(shí)現(xiàn)必要的操作符即可。不過具體的算法函數(shù)對迭代器類型是有要求的,這個(gè)需要自己把握。
在這個(gè)簡單的示例里面,直接用myVector的指針(mv._ptr)也是可以調(diào)用count_if的,因?yàn)镾TL通過模板偏特化技術(shù)使得迭代器也支持原生指針。不過既然把訪問元素都放到迭代器中了,我們就可以對所有的容器用統(tǒng)一的方式訪問了,而不用暴露每個(gè)容器的細(xì)節(jié)(myVector::_ptr):
//T為某種迭代器
template<class T>
void display(T it, T end){
T it1 = it;
while (it1 != end){
cout << *(it1++) << " ";
}
cout << endl;
cout << count_if(it,end, eq_10) << endl;
}
int main(){
//自定義類型
myVector<int> mv(10);
mv[3] = 10; mv[9] = 10;
//STL 容器
vector<int> v(10, 0);
v[3] = 10; v[9] = 10;
//vector 和 myVector底層實(shí)現(xiàn)有很大區(qū)別,但是可用同一個(gè)函數(shù)做遍歷等操作
display(mv.begin(), mv.end());
display(v.begin(), v.end());
getchar();
return 0;
}
迭代器賦予了容器更多的功能和通用性
補(bǔ)充知識:C++ 自定義迭代器(實(shí)現(xiàn)++遞增兩格)
//效果每次迭代器加移動(dòng)兩格
#pragma once
//MyIterator.h
#include <iterator>
#include <exception>
template<typename Container>
class MyIterator :public std::iterator<std::random_access_iterator_tag, typename Container::value_type>
{
protected:
Container& container;
typename Container::iterator pos;
public:
explicit MyIterator(Container& c) :container(c), pos(c.begin()){}
MyIterator(const MyIterator& rhs) :container(rhs.container),pos(rhs.pos) {}
MyIterator& operator =(const MyIterator& rhs)
{
throw_ex(rhs.container);
pos = rhs.pos;
return *this;
}
//--等就省略了...
MyIterator& operator ++()
{
auto tmp = container.end() - 1;
if (pos == tmp)
++pos;
else
pos += 2;
return *this;
}
bool operator ==(const MyIterator& rhs)const
{
try
{
if (&rhs.container == &container)
return pos == rhs.pos;
else
{
throw exception("對象錯(cuò)誤");
}
}
catch (exception &e)
{
cout << e.what();
exit(EXIT_FAILURE);
}
}
bool operator !=(const MyIterator& rhs)const
{
return !(*this == rhs);
}
typename Container::value_type & operator *()
{
return *pos;
}
void begin()
{
pos = container.begin();
}
void end()
{
pos = container.end();
}
private:
void throw_ex(const Container& c)
{
try
{
if (&c == &container)
return;
else
throw exception("Copy 構(gòu)造失敗");
}
catch (exception &e)
{
cout << e.what();
exit(EXIT_FAILURE);
}
}
};
//無法使用或添加vector<T> vec 成員函數(shù)vec.begin()或全局函數(shù)begin(vec)
//我們做個(gè)假冒的全局函數(shù) start(vec) over(vec)
template<typename Container>
MyIterator<Container> start(Container& c)
{
MyIterator<Container> mi(c);
mi.begin();
return mi;
}
template<typename Container>
MyIterator<Container> over(Container & c)
{
MyIterator<Container> mi(c);
mi.end();
return mi;
}
//main.cpp
#include <iostream>
#include <vector>
#include "MyIterator.h"
#include <list>
using namespace std;
//因繼承了iterator<std::random_access_iterator_tag,Container::value_type>才擁有此特性
template<typename Iterator>
void printIterator(const Iterator &It)
{
cout << typeid(typename iterator_traits<Iterator>::iterator_category).name() << endl;
}
int main()
{
vector<int> coll{ 1,2,3,4,5,6,7,8,9,10 };
MyIterator<decltype(coll)> myit(coll);
printIterator(myit);
for (; myit != over(coll); ++myit)
{
cout << *myit << ends;
}
system("pause");
return 0;
}
效果:

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方歡迎留言討論,望不吝賜教。
相關(guān)文章
C++中volatile和mutable關(guān)鍵字用法詳解
這篇文章主要介紹了C++中volatile和mutable關(guān)鍵字用法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
C++實(shí)現(xiàn)LeetCode(134.加油站問題)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(134.加油站問題),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++實(shí)現(xiàn)N個(gè)骰子的點(diǎn)數(shù)算法
這篇文章主要介紹了C++實(shí)現(xiàn)N個(gè)骰子的點(diǎn)數(shù)算法,用兩種方法實(shí)現(xiàn)了該功能,是非常實(shí)用的技巧,需要的朋友可以參考下2014-09-09

