C++ 实现自定义类型的迭代器操作
##动机
我们知道STL实现了很多算法(#include<algorithm>),如果项目是基于STL构建那么能够最大化使用现有代码当然是最好的。在STL中容器和算法之间的桥梁是迭代器。所以在定义好自定义类型的容器后,接下来就是迭代器的实现。
STL中的迭代器
迭代器模式是一种经典的设计模式,而STL的迭代器实现用到了模板的一些特性和技能,在这里稍微介绍一下
下面是STL中结构体iterator的定义,这么定义是给后面的算法多态和萃取时(具体见书中介绍)使用的。
其中的_Category 和_Ty 没有默认值,需要自己给参数的。
_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 //前向移动 : input_iterator_tag, _Mutable_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 }; //...
自定义迭代器
我希望迭代器有以下操作:*,++。另外还想要通过迭代器调用count_if函数。那看一下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); }
可以看到用到了++,!=,*。所以我们的迭代器需要把这些都给实现了。代码很简单:
#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;//实际的内容指针,通过该指针跟容器连接 };
自定义容器
下面给出个简单的数组容器,实现了数组的基本操作。并把刚刚定义的迭代器内置了
template<class T> class myVector{ public: typedef MyIterator<T> iterator;//所有类型迭代器用同一个名字,便于写出更通用的代码 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; };
##测试
定义一个vector和自定容器myVector,用迭代器去访问,并通过迭代器使用conunt_if函数,可以看到用法完全一样
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;
总结和思考
所以简单来说,如果想要定义自己容器的迭代器并想通过迭代器调用STL的算法函数的话。首先继承iteroter,然后实现必要的操作符即可。不过具体的算法函数对迭代器类型是有要求的,这个需要自己把握。
在这个简单的示例里面,直接用myVector的指针(mv._ptr)也是可以调用count_if的,因为STL通过模板偏特化技术使得迭代器也支持原生指针。不过既然把访问元素都放到迭代器中了,我们就可以对所有的容器用统一的方式访问了,而不用暴露每个容器的细节(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底层实现有很大区别,但是可用同一个函数做遍历等操作 display(mv.begin(), mv.end()); display(v.begin(), v.end()); getchar(); return 0; }
迭代器赋予了容器更多的功能和通用性
补充知识:C++ 自定义迭代器(实现++递增两格)
//效果每次迭代器加移动两格
#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("对象错误"); } } 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 构造失败"); } catch (exception &e) { cout << e.what(); exit(EXIT_FAILURE); } } }; //无法使用或添加vector<T> vec 成员函数vec.begin()或全局函数begin(vec) //我们做个假冒的全局函数 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; }
效果:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。如有错误或未考虑完全的地方欢迎留言讨论,望不吝赐教。
相关文章
- vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
- 这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
- 这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
pytorch::Dataloader中的迭代器和生成器应用详解
这篇文章主要介绍了pytorch::Dataloader中的迭代器和生成器应用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-30- 整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
- 本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
- 这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- 自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
- 这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
- 这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- 本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
- 今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
- 这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
PHP YII框架开发小技巧之模型(models)中rules自定义验证规则
YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24