c++ primer 学习笔记20 泛型算法 迭代器

使用泛型算法必须包含 algorithm 头文件:

    #include <algorithm>

标准库还定义了一组泛化的算术算法(generalizednumeric algorithm),其命名习惯与泛型算法相同。使用这些算法则必须包含 numeric 头文件:

    #include <numeric>

除了少数例外情况,所有算法都在一段范围内的元素上操作,我们将这段范围称为“输出范围(input range)”。带有输入范围参数的算法总是使用头两个形参标记该范围。这两个形参是分别指向要处理的第一个元素和最后一个元素的下一位置的迭代器。

理解算法的最基本方法是了解该算法是否读元素、写元素或者对元素进行重新排序。

11.2.1.只读算法

许多算法只会读取其输入范围内的元素,而不会写这些元素。find 就是一个这样的算法。另一个简单的只读算法是 accumulate,该算法在 numeric 头文件中定义。

假设 vec 是一个 int 型的 vector 对象,下面的代码:

     // sum the elements in vec starting thesummation with the value 42

     int sum = accumulate(vec.begin(),vec.end(), 42);

将 sum 设置为 vec 的元素之和再加上 42。

find_first_of的使用

find_first_of 函数,这个算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。

假设 roster1 和 roster2 是两个存放名字的 list对象,可使用 find_first_of 统计有多少个名字同时出现在这两个列表中:

 // program for illustration purposes only: 
     // there are much faster ways to solve this problem 
     size_t cnt = 0; 
     list<string>::iterator it = roster1.begin(); 
     // look in roster1 for any name also in roster2 
     while   ((it = find_first_of(it, roster1.end(),roster2.begin(), roster2.end()))
!= roster1.end()) {
++cnt; 
// we got a match, increment it to look in the rest of roster1 
++it; }
cout << "Found " << cnt  << " names on both rosters" << endl;

11.2.2.写容器元素的算法

一些算法写入元素值。在使用这些算法写元素时要当心,必须确保算法所写的序列至少足以存储要写入的元素。

写入到输入序列的一个简单算法是 fill 函数,考虑如下例子:

     fill(vec.begin(), vec.end(), 0); // reseteach element to 0

     // set subsequence of the range to 10

     fill(vec.begin(), vec.begin() +vec.size()/2, 10);

不检查写入操作的算法

fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素的空容器上调用 fill_n 函数(或者类似的写元素算法)。

     vector<int> vec; // empty vector

     // disaster: attempts to write to 10(nonexistent) elements in vec

     fill_n(vec.begin(), 10, 0);

对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。

使用 back_inserter 的程

序必须包含 iterator 头文件。

back_inserter 函数是迭代器适配器。与容器适配器(第 9.7 节)一样,迭代器适配器使用一个对象作为实参,并生成一个适应其实参行为的新对象。

vector<int>vec; // empty vector

     // ok: back_inserter creates an insertiterator that adds elements to vec

     fill_n (back_inserter(vec), 10, 0); //appends 10 elements to vec

现在,fill_n 函数每写入一个值,都会通过back_inserter 生成的插入迭代器实现。效果相当于在 vec 上调用 push_back,在 vec 末尾添加 10 个元素,每个元素的值都是 0。

写入到目标迭代器的算法

第三类算法向目标迭代器写入未知个数的元素。正如 fill_n 函数一样,目标迭代器指向存放输出数据的序列中第一个元素。这类算法中最简单的是 copy 函数。copy 带有三个迭代器参数:头两个指定输入范围,第三个则指向目标序列的一个元素。传递给 copy 的目标序列必须至少要与输入范围一样。假设 ilst 是一个存放 int 型数据的 list 对象,可如下将它 copy 给一个 vector 对象:

     vector<int> ivec; // empty vector

     // copy elements from ilst into ivec

     copy (ilst.begin(), ilst.end(),back_inserter(ivec));

copy 从输入范围中读取元素,然后将它们复制给目标 ivec。

当然,这个例子的效率比较差:通常,如果要以一个已存在的容器为副本创建新容器,更好的方法是直接用输入范围作为新构造容器的初始化式:

     // better way to copy elements from ilst

     vector<int> ivec(ilst.begin(),ilst.end());

算法的 _copy 版本

replace 算法就是一个很好的例子。该算法对输入序列做读写操作,将序列中特定的值替换为新的值。该算法带有四个形参:一对指定输入范围的迭代器和两个值。每一个等于第一值的元素替换成第二个值。

     // replace any element with value of 0 by42

     replace(ilst.begin(), ilst.end(), 0, 42);

这个调用将所有值为 0 的实例替换成 42。如果不想改变原来的序列,则调用 replace_copy。这个算法接受第三个迭代器实参,指定保存调整后序列的目标位置。

     // create empty vector to hold thereplacement

     vector<int> ivec;

 

     // use back_inserter to grow destinationas needed

     replace_copy (ilst.begin(), ilst.end(),

                  back_inserter(ivec), 0, 42);

调用该函数后,ilst 没有改变,ivec 存储 ilst 一份副本,而 ilst 内所有的 0 在 ivec 中都变成了 42。

11.3. 再谈迭代器

标准库所定义的迭代器不依赖于特定的容器。事实上,C++ 语言还提供了另外三种迭代器:

•  插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。

•  iostream 迭代器:这类迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。

•  反向迭代器:这类迭代器实现向后遍历,而不是向前遍历。所有容器类型都定义了自己的 reverse_iterator 类型,由 rbegin 和 rend 成员函数返回。

上述迭代器类型都在 iterator 头文件中定义。

11.3.1. 插入迭代器

back_inserter 函数是一种插入器。插入器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。通过插入迭代器赋值时,迭代器将会插入一个新的元素。C++ 语言提供了三种插入器,其差别在于插入元素的位置不同。

•  back_inserter,创建使用 push_back 实现插入的迭代器。

•  front_inserter,使用 push_front 实现插入。

•  inserter,使用 insert 实现插入操作。除了所关联的容器外,inserter还带有第二实参:指向插入起始位置的迭代器。

front_inserter需要使用 push_front

front_inserter 的操作类似于back_inserter:该函数将创建一个迭代器,调用它所关联的基础容器的 push_front 成员函数代替赋值操作。

 

只有当容器提供 push_front 操作时,才能使用front_inserter。在 vector 或其他没有 push_front 运算的容器上使用 front_inserter,将产生错误。

inserter将产生在指定位置实现插入的迭代器

inserter 适配器提供更普通的插入形式。这种适配器带有两个实参:所关联的容器和指示起始插入位置的迭代器。

     // position an iterator into ilst

     list<int>::iterator it = find(ilst.begin(), ilst.end(), 42);

     // insert replaced copies of ivec at thatpoint in ilst

     replace_copy (ivec.begin(),ivec.end(),inserter (ilst, it), 100, 0);

首先用 find 定位 ilst 中的某个元素。使用inserter 作为实参调用 replace_copy,inserter 将会在 ilst 中由 find 返回的迭代器所指向的元素前面插入新元素。而调用 replace_copy 的效果是从 ivec 中复制元素,并将其中值为 100 的元素替换为 0 值。ilst 的新元素在 it 所标明的元素前面插入。

11.3.2.iostream 迭代器

虽然 iostream 类型不是容器,但标准库同样提供了在 iostream 对象上使用的迭代器:istream_iterator 用于读取输入流,而 ostream_iterator 则用于写输出流(表 11.1)。这些迭代器将它们所对应的流视为特定类型的元素序列。使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。


流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。此外,可比较两个 istream 迭代器是否相等(或不等)。而 ostream 迭代器则不提供比较运算(表 11.2)。


流迭代器都是类模板:任何已定义输入操作符(>>操作符)的类型都可以定义 istream_iterator。类似地,任何已定义输出操作符(<< 操作符)的类型也可定义 ostream_iterator。

在创建流迭代器时,必须指定迭代器所读写的对象类型:

istream_iterator<int> cin_it(cin);    // reads ints1 from cin 
     istream_iterator<int> end_of_stream;  // end iterator value 
     // writes Sales_items from the ofstream named outfile 
     // each element is followed by a space  ofstream outfile; 
     ostream_iterator<Sales_item> output(outfile, " "); 

ostream_iterator对象必须与特定的流绑定在一起。在创建istream_iterator 时,可直接将它绑定到一个流上。另一种方法是在创建时不提供实参,则该迭代器指向超出末端位置。ostream_iterator 不提供超出末端迭代器。

istream_iterator对象上的操作

构造与流绑定在一起的istream_iterator 对象时将对迭代器定位,以便第一次对该迭代器进行解引用时即可从流中读取第一个值。

考虑下面例子,可使用istream_iterator 对象将标准输入读到 vector 对象中。

istream_iterator<int> in_iter(cin); // read ints from cin 
     istream_iterator<int> eof; // istream "end" iterator 
     // read until end of file, storing what was read in vec 
     while (in_iter != eof) 
             // increment advances the stream to the next value 
             // dereference reads next value from the istream 
             vec.push_back(*in_iter++);










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值