zoukankan      html  css  js  c++  java
  • 迭代器(转)

    插入迭代器
     
       插入迭代器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定的容器中插入元素。通过插入迭代器赋值时,迭代器将会插入一个新的元素。C++语言提供了三种插入器,其差别在于插入元素的位置不同:
       1)back_inserter,创建使用push_back实现插入的迭代器;
       2)front_inserter,使用push_front实现插入;
       3)inserter,使用insert实现插入操作。除了所关联的容器外,inserter还带有第二个实参:指向插入起始位置的迭代器。
     
       front_inserter的操作类似于back_inserter:该函数将创建一个迭代器,调用它所关联的基础容器的push_front成员函数代替赋值操作。注意:只有当容器提供push_front操作时,才能使用front_inserter。在vector或其他没有push_front 运算的容器上使用front_inserter,将产生错误。
     
       inserter将产生在指定位置实现插入的迭代器,inserter总是在它的迭代器参数所标明的位置前面插入新元素。看看下面的例子:
       list<int> ilst, ilst2, ilst3; //empty lists
       // after this loop ilst contains: 1 2 3 4
       for (list<int>::value_type i = 0; i != 4; ++i)
          ilst.push_front(i + 1);
       // after copy ilst2 contains: 4 3 2 1
       copy (ilst.begin(), ilst.end(), front_inserter(ilst2));
       // after copy ilst3 contains: 1 2 3 4
       copy (ilst.begin(), ilst.end(), inserter(ilst3, ilst3.begin()));
     
    iostream 迭代器
     
       虽然iostream类型不是容器,但标准库同样提供了在iostream对象上使用的迭代器:istream_iterator用于读取读入流,而 ostream_iterator用于写输出流。这些迭代器将它们所对应的流视为特定类型的元素序列。使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。
     
     istream_iterator<T> in(strm);  创建从输入流strm中读取T类型对象的istream_iterator对象
     istream_iterator<T> in;  istream_iterator对象的超出末端迭代器
     ostream_iterator<T> out(strm);  创建将T类型的对象写到输出流strm的ostream_iterator对象
     ostream_iterator<T> out(strm, delim);  创建将T类型的对象写到输出流strm的ostream_iterator对象,在写入过程中使用delim作为元素的分隔符。delim是以空字符结束的字符数组
     
       流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。此外,可比较两个istream迭代器是否相等(或不等)。而ostream迭代器则不提供比较运算。
     
     it1 == it2  比较两个istream_iterator是否相等(不等)。迭代器读取的必须是
     相同的类型。如果两个迭代器都是end值,则它们相等。对于两个都不
     it1 != it2  指向流结束位置的迭代器,如果它们使用同一个输入流构造,则它们
     相等。
     *it  返回从流中读取的值
     it->mem  是(*it).mem的同义词。返回从流中读取的对象的mem成员
     ++it  通过使用元素类型提供的>>操作符从个输入流中读取下一个元素值,
     使迭代器向前移动。通常,前缀版本使迭代器在流中向前移动,并返
     回对加1后的迭代器的引用。
     it++  而后缀版本使迭代器在流中向前移动后,返回原值。
     
       流迭代器是类模板:任何已定义输入操作符(>>操作符)的类型都可以定义istream_iterator。类似地,任何已定义输出操作符(<<操作符)的类型也可以ostream_iterator。
     
    istream_iterator使用举例:
    #include <iostream>
    #include <vector>
    #include <iterator>
    using namespace std;

    int main() {
        istream_iterator<int> in_iter(cin);
        istream_iterator<int> eof;

        
    //vector<int> vec(in_iter, eof); //do the same work as following loop

        vector<int> vec;
        while (in_iter != eof)
            vec.push_back(*in_iter++);
        
        vector<int>::const_iterator it = vec.begin();
        for(; it != vec.end(); ++it)
            cout<<*it<<endl;
        
        return 0;
    }
     
     
    ostream_iterator使用举例:

    #include <iostream>
    #include <iterator>
    using namespace std;

    int main() {
        ostream_iterator<string> out_iter(cout, "\n");
        istream_iterator<string> in_iter(cin), eof;
        while (in_iter != eof)
            *out_iter++ = *in_iter++;
        
        return 0;
    }

     
       流迭代器的限制:
       1)不可能从ostream_iterator对象读入,也不可能写到istream_iterator对象中;
       2)一旦给ostream_iterator对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator对象中每个不同的值都只能正好输出一次。
       3)ostream_iterator没有->操作符。
     
       与算法一起使用流迭代器,如下面的示例实现从标准输入读取一些数,然后将不重复的数写到标准输出:

    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <algorithm>
    using namespace std;

    int main() {
        istream_iterator<int> in_it(cin), eof;
        vector<int> vec(in_it, eof);
        sort(vec.begin(), vec.end());
        ostream_iterator<int> out_it(cout, " ");
        unique_copy(vec.begin(), vec.end(), out_it);
        
        return 0;
    }

     
    反向迭代器
     
       反向迭代器是一种反向遍历容器的迭代器。也就是,从最后一个元素到第一个元素遍历容器。反向迭代器将自增(和自减)的含义反过来了:对于反向迭代器,++运算将访问前一个元素,而--运算则访问下一个元素。
     
       begin(), end(), rbegin(), rend()与容器序列关系示意图如下:

     

       1)反向迭代器需要使用自减操作符:标准容器上的迭代器(reverse_iterator)既支持自增运算,也支持自减运算。但是,流迭代器由于不能反向遍历流,因此流迭代器不能创建反向迭代器。
       2)可以通过reverse_iterator::base()将反向迭代器转换为普通迭代器使用,从逆序得到普通次序。如下面的例子所示:

    #include <iostream>
    #include <string>
    #include <iterator>
    #include <algorithm>
    using namespace std;

    int main() {
        string str = "this 'sentence' is a test";
        cout<<"String: "<<str<<endl;
        
        string::iterator it1 = find(str.begin(), str.end(), '\'');
        string::iterator it2 = find(++it1, str.end(), '\'');
        
    // output: sentence
        cout<<"B-E: "<<string(it1, it2)<<endl;
        
        string::reverse_iterator rit1 = find(str.rbegin(), str.rend(), '\'');
        string::reverse_iterator rit2 = find(++rit1, str.rend(), '\'');
        
    // output: ecnetnes
        cout<<"R-B-E 1: "<<string(rit1, rit2)<<endl;
        
    // output: sentence
        cout<<"R-B-E 2: "<<string(rit2.base(), rit1.base())<<endl;
        
        return 0;
    }

     
    const 迭代器
     
       在标准库中,有输入范围的泛型算法要求其两个迭代器类型完全一样,包括const属性。要么都是const,要么都是非const,否则无法通过编译。同样,它们的返回值迭代器也与参数类型保持一致。
     
    迭代器分类
     
       不同的迭代器支持不同的操作集,而各种算法也要求相应的迭代器具有最小的操作集。因此,可以将算法的迭代器分为下面五类:
     
     输入迭代器
    (input iterator)
     读,不能写。支持的操作集:==, !=, 前缀++, 后缀++, *, ->。例如,find, accumulate算法要求这类迭代器。
     输出迭代器
    (output iterator)
     写,不能读。支持的操作集:前缀++, 后缀++, *(只能出现在赋值运算的左操作数上)。推出迭代器要求每个迭代器必须正好写入一次。例如,ostream_iterator是输出迭代器,copy算法使用这类迭代器。
     前向迭代器(forward iterator)  读和写,支持输入迭代器和输出迭代器提供的所有操作,还支持对同一个元素的多次读写。例如,replace算法需要这种迭代器。
     双向迭代器(bidirectional iterator)  读和写,除了支持前向迭代器的所有操作,还支持前缀--和后缀--,即支持双向遍历容器。例如,reverse算法要求这类迭代器。标准库容器中提供的迭代器都至少达到双向迭代器的要求。
     随机访问迭代器(random-access iterator)  读和写。提供在常量时间内访问容器任意位置的功能。支持完整的迭代器操作集:1)关系运算:==, !=, <, <=, >, >=;2)算术运算:it + n, it - n, it += n, it -= n以及it1 - it2;3)下标运算:it[n],等价于*(it + n)。需要随机访问迭代器的泛型算法包括sort算法。例如,vector, deque, string迭代器是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。
     
       除了输出迭代器,其他类别的迭代器形成了一个层次结构:需要低级类别迭代器的地方,可使用任意一种更高级的迭代器。例如,对于需要输入迭代器的算法,可传递前向、双向或随机访问迭代器调用该算法。而反之则不行。注意:向算法传递无效的迭代器类别所引起的错误,无法保证会在编译时被捕获到。
     
       map, set, list类型提供双向迭代器,而string, vector和deque容器上定义的迭代器都是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。istream_iterator是输入迭代器,ostream_iterator是输出迭代器。
     
       另外,虽然map和set类型提供双向迭代器,但关联容器只能使用这部分算法的一个子集。因为关联容器的键是const对象。因此,关联容器不能使用任何写序列元素的算法。只能使用与关联容器绑在一起的迭代器来提供用于读操作的实参。因此,在处理算法时,最好将关联容器上的迭代器视为支持自减运算的输入迭代器,而不是完整的双向迭代器。
     

    泛型算法的结构
       就像所有的容器都建立在一致的设计模式上一样,算法也具有共同的设计基础。
     
       算法最基本的性质是需要使用的迭代器种类。
       另一种算法分类方法是前面介绍的按实现的功能分类:只读算法,不改变元素的值和顺序;给指定元素赋新值的算法;将一个元素的值移给另一个元素的算法。
       另外,算法还有两种结构上的算法模式:一种模式是由算法所带的形参定义;另一种模式则通过两种函数命名和重载的规范定义。
     
    算法的形参模式
     
       大多数算法采用下面四种形式之一:
          alg (beg, end, other parms);
          alg (beg, end, dest, other parms);
          alg (beg, end, beg2, other parms);
          alg (beg, end, beg2, end2, other parms);
    其中,alg是算法名,[beg, end)是输入范围,beg, end, dest, beg2, end2都是迭代器。
     
       对于带有单个目标迭代器的算法:dest形参是一个迭代器,用于指定存储输出数据的目标对象。算法假定无论需要写入多少个元素都是安全的。注意:调用这类算法时,算法是将输出内容写到容器中已存在的元素上,所以必须确保输出容器中有足够大的容量存储输出数据,这也正是通过使用插入迭代器或者 ostream_iterator来调用这些算法的原因。
     
       对于带第二个输入序列的算法:beg2和end2标记了完整的输出范围。而只有beg2的算法将beg2视为第二个输入范围的首元素,算法假定以beg2开始的范围至少与beg和end指定的范围一样大。
     
    算法的命名规范
     
       包括两种重要模式:第一种模式包括测试输入范围内元素的算法,第二种模式则应用于输入范围内元素的重新排序的算法。
     
       1)区别带有一个值或一个谓词函数参数的算法版本
     
       很多算法通过检查其输入范围内的元素实现其功能。这些算法通常要用到标准关系操作符:== 或 < 。其中的大部分算法都提供了第二个版本的算法,允许程序员提供比较或测试函数取代默认的操作符的使用。
     
       例如, 排序算法默认使用 < 操作符,其重载版本带有一个额外的形参,表示取代默认的 < 操作符。
          sort (beg, end);         // use < operator to sort the elements
          sort (beg, end, comp);   // use function named comp to sort the elements
     
       又如,查找算法默认使用 == 操作符。标准库为这类算法提供另外命名的(而非重载的)版本,带有谓词函数形参。对于带有谓词函数形参的算法,其名字带有后缀 _if:
          find (beg, end, val);      // find first instance of val in the input range
          find_if (beg, end, pred);  // find first instance for which pred is true
    标准库为这类算法提供另外命名的版本,而非重载版本,原因在于这两种版本的算法带有相同的参数个数,容易导致二义性。
     
       2)区别是否实现复制的算法版本
      
       默认情况下,算法将重新排列的写回其范围。标准库也为这类算法提供了另外命名的版本,将元素写到指定的输出目标。此版本的算法在名字中添加 _copy后缀,例如:
          reverse (beg, end);
          reverse_copy (beg, end, dest);
    第一个版本将输入序列中的元素反向重新排列;而第二个版本将复制输入序列中的元素,并将它们以逆序存储到dest开始的序列中。
     

    容器特有的算法
       list容器上的迭代器是双向的,而不是随机访问类型。由于list容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法。如sort 类算法。其它有些算法,如merge, remove, reverse, unique等,虽然可以用在list上,但性能太差。list容器结合自己的结构专门实现了更为高效的算法。因此,对于list对象,应该优先使用 list容器特有的成员版本,而不是泛型算法。
         
       下表列出了list容器特有的操作:
     
     lst.merge(lst2)
     lst.merge(lst2, comp)

     将lst2的元素合并到lst中。这两个list对象必须都已排序。合并后,lst2中元素被删除,为空。返回void类型

     lst.remove(val)
     lst.remove_if(unaryPred))
     调用lst.erase删除所有等于指定值或满足谓词的元素,返回void类型。
     lst.reverse()  反向排列lst中的元素
     lst.sort()   对lst中的元素排序
     lst.splice(it, lst2)
     lst.splice(it, lst2, it2)
     lst.splice(it, beg, end)
     将lst2的元素移到lst中迭代器it所指向的元素前面。在lst2中删除移出的元素。第一个版本将lst2的所有元素移到lst中,合并后,lst2为空。lst和lst2不能是同一个list对象。第二个版本只移动it2所指向的元素,这个元素必须是lst2的元素。在这种情况下,lst和lst2可以是同一list对象。第三个版本移动迭代器beg和end标记的范围内的元素。这两个可以标记任意list对象的范围,包括 lst。当它们指定lst的一段范围时,如果it也指向这个范围内的一个元素,则该操作未定义。
     lst.unique()
     lst.unique(binaryPred)
     调用erase删除同一值的连续版本。第一个版本使用 == 操作符判断元素是否相等;第二个版本则使用指定的谓词函数实现判断。
     
       list容器特有的算法与其泛型算法版本之间有两个重要的差别:1)remove和unique的list版本修改了其关联的基础容器:真正删除了指定的元素;2)list容器提供的merge和splice操作会破坏它们的实参。使用泛型算法的merge版本,合并的序列将写入目标迭代器指向的对象,而它的两个输入序列保持不变。
  • 相关阅读:
    Quartz.net官方开发指南 第十一课: 高级(企业级)属性
    .NET开源项目:Tomboy
    开源的作业调度框架 Quartz.NET
    DinnerNow.net: 微软最新技术集成示例
    将Quartz.NET集成到 Castle中
    评论:微软的SOA战略
    行为驱动开发Behaviour Driven Development
    Mono 1.2.5 发布,提供对IronPython和DLR的支持
    Quartz.net官方开发指南 第二课:Jobs And Triggers
    数据库厂商提供的 Providers for ASP.NET
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/2701777.html
Copyright © 2011-2022 走看看