《白话C++》第10章 Page115 10.5.2 迭代器辅助操作

10.5.2 迭代器辅助操作

1.前进、后退

前面提到“++”与“--”可以用在多数迭代器上,但“+=”与“-=”就只能支持随机迭代器

#include <iterator>
void advance(InputItrator& pos, Dist n);

advance可以让一个迭代器(pos),前进n步,如果n是负数,并且pos又支持双向,那就是后退n步。

InputItrator 和 Dist在这里都是函数模板参数,Dist通常就是一个整数。advance会识别出pos是否支持随机访问,如果支持,那就是一次 pos += n的操作,否则,会调用以下代码(假设n为正数):

for(int i = 0; i < n; ++i)  ++pos;

【危险】:不负责的导游

无论是包装好的advance()操作,还是直接的 ++, +=, --, -=操作,被前进或后退的迭代器,都不会主动检查自己是否已经越界了。

2.计算迭代器之间的距离

两个迭代器都指向同一个容器(并且这个容器没有在变动),那么可以计算这两个迭代器之间的距离(隔几个元素):

#include <iterator>
void distance(InputIterator pos1, InputIterator pos2);

pos1 和 pos2 必须指向同一个容器。如果是随机迭代器,则简单返回  pos2 - pos1;如果不是,该函数一直执行++pos,直到它和pos2相遇,可见,pos2 必须确保在 pos1 相等或其后的位置。

distance应用的典型场景,是在使用find算法查找到一个确切的位置之后,用来计算它距离前一个位置有多远。

3.交换两个迭代器所指向的值

#include <algorithm> //在"算法"中声明
void iter_swap(ForwardIterator1 pos1, ForwardIterator2 pos2);

这个函数不是在交换另个迭代器的指向,而是在交换二者所指向的值。

所以允许pos1和pos2不一定指向同一容器甚至两个迭代器的类型都可以不一致,只要它们所指向的内容可以交换(互相赋值)。

10.5.3  喜欢兼职的迭代器

1.逆向迭代器

倒着遍历一个容器,这个需求非常普遍。如果使用“双向迭代器”,可以从容器的尾部(如果有提供的话)开始,然后使用“--”操作,一步步退回来:

list <int> li {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

list <int >::const_iterator it = li.end(); //尾部

//不能是for(--if;...),也不能是for(;it != begin();--it)
for(; it != li.begin();)
{
    --it;
    cout << *it << ",";
}

it一开始指向容器的尾部节点一个虚的节点,位于最后一个节点之后)。如果这个容器是空的,那么begin()和end()是相等的,所以for循环的判断条件在空容器的情况下,也是适用的。

这个过程显然没有正向遍历来得直观,所以

(Reverse)逆向迭代器”包装了正常的迭代器,重新定义了递增运算和递减运算,让二者的行为正好相反(镜像)。

逆向迭代器,必须附身在一个“双向迭代器”身上(因为内部通过调用递减操作实现)。

支持逆向遍历的容器,通常都提供了rbegin()和rend(),作用就像begin()和end(),只不过rbegin()其实是容器的尾端,而rend()是容器的开端。

...

list <int>::const_reverse_iterator it2 = li.rbegin();

//回到正常的for循环写法了,舒服!
for(; it2 != li.rend(); ++ it2)
{
    cout << *it2 << ",";
}

rbegin()的位置相当于end(),但透过rbegin()访问一个数据,访问到的是其前面的那个元素(因为我们不能在end()位置上访问数据),如图10-13所示。

正向迭代器表里如一,表现和实际指向的都是同一个节点。逆向迭代器逻辑上指向n位置上的节点,但实际访问的是n-1位置上的节点;

【危险】:从正向迭代器,构造一个逆向迭代器

可以通过一个正向迭代器,构造出一个逆向迭代器,不过由于上述原因,经过构造转换之后,访问该逆向迭代器,得到的是其前一节点的位置。

举个例子:如果将begin()转换成逆向迭代器,就得到rend(),可不能访问它的值,因为它实际指向的内容非法。

2.插入型的迭代器

插入型迭代器,可以向容器插入数据。

根据可插入的位置,“插入型迭代器”又区分为“Front-Inserter(前端插入器)”、“Back-Inserter(后端插入器)”和“General-Inserter(通用插入器)”。

提供有push_back()操作的容器都可适用“Back-Inserter”。像标准库中的vectors,deques,lists,strings,等等。以vector为例:

//同样需要包含该头文件
#include <vector> 

void test_back_inserter()
{
    vector <int> v; //本来空无一物

    back_insert_iterator <vector <int>> it_inserter(v);

    for(int i = 0; i < 10; ++i)
    {
        //会调用v.push_back(i+1)
        * it_inserter = i + 1; 
    }

    //现在,v里面有10个元素
    ...
}

重点在“ * it_inserter = i + 1;”这一句。它会调用 v.push_back(i + 1)。、

back_insert_iterator <typename T>的模板参数是一个具体的容器类型,而构造参数则是一个容器。

front_insert_iterator <typename T>与之类似,只是改为要求容器提供push_front()操作,STL中此类容器有  deques  和  lists  等。

从插入效果看,由于每次都在最前面插入,如果是无需容器,则存储元素的次序刚好和插入次序相反(有序容器会根据插入内容自动排序)。

所有提供insert()操作的容器都支持“General-Inserter”。通用性插入器可在容器的各个位置插入。其构造函数也有所不同,必须指明插入位置:

insert_iterator <typename T, typename P> iter(T& c, P pos);

T和c 仍然是容器的类型的对象,pos 是一个迭代器,用于表示要插入的位置。

#include <list>
...

list <int> l;

//构造一个通用型插入迭代器,首先在begin()处
insert_iterator <list<int>> it_inserter(l, l.begin());
*it_inserter = 1;
*it_inserter = 2;

list <int>::iterator it = l.begin();
++ it;

//再构造一个通用型插入迭代器,在it处
insert_iterator <list<int>> it_inserter2(l, it);
*it_inserter2 = 3;
*it_inserter2 = 4;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值