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;
}

效果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方欢迎留言讨论,望不吝赐教。

(0)

相关推荐

  • C++begin和end运算符的返回迭代器的类型如何判断?

    begin和end返回的具体类型应该由对象是否是常量进行确定,如果对象是常量,则这两个函数返回const_iterator; 如果对象不是常量,则这个函数返回iterator类型.下面利用一个超级简单的小程序进行验证二者的类型,源代码如下: #include <iostream> #include <vector> using namespace std; int main() { vector<int> ivec; const vector<int> cv

  • c++迭代器失效的情况汇总

    一.序列式容器(数组式容器) 对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效.这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置.所以不能使用erase(iter++)的方式,还好erase方法可以返回下一个有效的iterator. for (iter = cont.begin(); iter != cont.end();) { (*it)->doSome

  • 浅谈c++ stl迭代器失效的问题

    之前看<C++ Primier>的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究.今天写程序的时候遇到了这个问题. 1 莫名其妙的Erase 最初我的程序是酱紫的,别说话,我知道这样是有问题的,可这样是最直观的想法 int arr[]={0,1,2,3,4,5,6,7,8,9,10}; vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));for (auto it = a.begin(); it !=

  • C++ 模拟实现list(迭代器)实现代码

    C++ 模拟实现list(迭代器) 实现代码: #pragma once; #include <assert.h> #include<iostream> #include <assert.h> using namespace std; template<class T> struct __ListNode { T _data; __ListNode<T>* _next; __ListNode<T>* _prev; __ListNode

  • C++迭代器介绍(iterator、const_iterator、reverse_interator、const_reverse_interator)

    概念:C++的一种机制,用来遍历标准模板库容器中的元素,是一种"智能"指针 一.迭代器的特点 迭代器是一种智能指针,具有遍历复杂数据结构的能力 不同的容器有不一样的内部结构,因此会有一样的迭代器类型 迭代器定义后,并不属于某一实例容器对象,只要是属于该迭代器类型的容器类型都可用 迭代器的分类 C++的STL定义了5种迭代器 输入迭代器:提供了对其指向元素的只读操作以及前++和后++操作符 输出迭代器:提供了对其指向元素的写操作和++操作符 向前迭代器:具有++操作符 双向迭代器:既具有

  • 详解C++中的vector容器及用迭代器访问vector的方法

    vector vector是相同类型对象的集合.集合中的每个对象有个对应的索引.vector常被称为容器(container). 为了使用vector,需要: #include <vector> using std::vector; vector是一个类模版(class template).C++有函数模版和类模版.模版本身不是函数或类,必须通过指定 类型让编译器去实例化(instantiation)它.比如vector<int> ivec. vector是模版,不是类型.从vec

  • C++设计模式之迭代器模式(Iterator)

    迭代器在STL运用广泛,类似容器的迭代已经成为其重要特性,而迭代器模式则是利用迭代器概念进行的抽象运用,迭代器模式运用广泛和有用,因为其能够不考虑数据的存储方式,而是直接面对数据进行迭代,也就是说我们不用考虑集合是数组(或vector).链表.栈还是队列,而是通过统一的接口进行顺序的访问. 作用 迭代器模式提供了一种顺序访问容器中元素的方法,而无需了解器内部的类型和结构,该模式的核心思想将访问和遍历容器对象的功能交给一个外部的迭代器对象,该迭代器定义了访问聚合对象的接口, 类视图 实现 clas

  • C++ 实现自定义类型的迭代器操作

    ##动机 我们知道STL实现了很多算法(#include<algorithm>),如果项目是基于STL构建那么能够最大化使用现有代码当然是最好的.在STL中容器和算法之间的桥梁是迭代器.所以在定义好自定义类型的容器后,接下来就是迭代器的实现. STL中的迭代器 迭代器模式是一种经典的设计模式,而STL的迭代器实现用到了模板的一些特性和技能,在这里稍微介绍一下 下面是STL中结构体iterator的定义,这么定义是给后面的算法多态和萃取时(具体见书中介绍)使用的. 其中的_Category 和_

  • Mybatis实现自定义类型转换器TypeHandler的方法

    先给大家简单介绍下mybatis MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 此文其实是java操作Oracle类型XMLType总结二:使用Mybatis附带的一篇小结. Mybatis实现自定义的转换器,

  • Python3 类型标注支持操作

    typing为Python的一个标注库,此默认支持PEP 484和PEP 526指定的类型提示.最基本的支持由Any.Union.Tuple.Callable.TypeVar和Generic类型组成. 有关完整的规范,请参阅PEP 484,有关任何类型提示的简单介绍,请参阅PEP 483. 举个栗子,函数接收并返回一个字符串,如下所示: def func(name: str) -> str: return "Hello" + name 在函数func中,参数预期是str类型,并且

  • JavaScript之自定义类型

    1.直接创建模式.这是最简单也是最直接的一种模式,首先创建一个引用类型的对象,然后为其添加自定义属性和方法.示例代码如下: 复制代码 代码如下: var person = new Object(); person.name = "Sam"; person.age = 16; person.speak = function(){ alert(this.name + "is " + this.age + "years old"); } person.

  • JSP 自定义注解及记录操作日志

    JSP 自定义注解及记录操作日志 Spring的配置文件 <aop:aspectj-autoproxy /> 日志拦截器 package com.vem.interceptor; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.anno

  • Javascript 自定义类型方法小结

    1. 定义类型 复制代码 代码如下: function UserObject(parameter) { } parameter 可省略,相当于C#中构造函数参数. 2. 实例化自定义类型 复制代码 代码如下: <script type="text/javascript"> function userobject(parameter){ } //myobject is now an object of type userobject! var myobject=new use

  • 简要分析Java的Hibernate框架中的自定义类型

    最近看到hibernate的自定义类型,这个以前没接触过,在这里记录一下,当是对自己知识的巩固,也让没有接触过的朋友一起学习研究一番.  1)自定义类型,顾名思义,当然就是由于内部的类型不满足需求,而自己来进行实现的类型.这种情况不多,但我们还是有必要学习一下,技多不压身嘛.也学习一下,别人在做框架的时候是怎么去考虑的,怎么去思考扩展性的.  自定义类型有两个方法来实现,一种是实现UserType,另外一种实现CompositeUserType,另外可能还有一些方法,但我暂时没用到,先不讲了.

  • javascript中对Date类型的常用操作小结

    javascript中对Date类型的常用操作小结 /** 3. * 日期时间脚本库方法列表: 4. * (1)Date.isValiDate:日期合法性验证 5. * (2)Date.isValiTime:时间合法性验证 6. * (3)Date.isValiDateTime:日期和时间合法性验证 7. * (4)Date.prototype.isLeapYear:判断是否闰年 8. * (5)Date.prototype.format:日期格式化 9. * (6)Date.stringToD

  • MyBatis自定义类型转换器实现加解密

    需求场景:当数据库中保存的部分数据需要加密,页面需要正常显示时.这是就需要我们自定义类型转换器,在Mybatis执行SQL得到结果时,通过自定义类型转换器将CHAR或者VARCHAR2进行加解密处理,Java代码如下: /**自定义typeHandler<br/> * 1 插入数据库, 加密 * 2 查询,解密 * @author Administrator * */ public class CryptTypeHandler implements TypeHandler<CryptTy

  • C#自定义类型强制转换实例分析

    本文实例讲述了C#自定义类型强制转换的用法.分享给大家供大家参考.具体分析如下: 先来举一个小例子 类定义: public class MyCurrency { public uint Dollars; public ushort Cents; public MyCurrency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents; } public override string ToString()

随机推荐