迭代器.docx

上传人:b****6 文档编号:8684766 上传时间:2023-02-01 格式:DOCX 页数:15 大小:36.94KB
下载 相关 举报
迭代器.docx_第1页
第1页 / 共15页
迭代器.docx_第2页
第2页 / 共15页
迭代器.docx_第3页
第3页 / 共15页
迭代器.docx_第4页
第4页 / 共15页
迭代器.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

迭代器.docx

《迭代器.docx》由会员分享,可在线阅读,更多相关《迭代器.docx(15页珍藏版)》请在冰豆网上搜索。

迭代器.docx

迭代器

c++iterator(迭代器)分类及其使用

前言:

  以下的内容为我阅读c++沉思录18,19,20章的笔记以及自己的想法.

正文:

  总所周知,c++的stl中提出了iterator的概念,这是C所没有的.在一般的使用中,iterator的行为很像c内建的指针.而在java和c#中索性就直接取消了指针,而采用类似iterator的做法来代替了指针.很多编程人员在使用iterator的时候也仅仅把他当作了指针的一个变体而没有多加注意.

  不过既然是学习,那我们在使用的时候也要知道其存在的原因,其分类以及用法吧.

  首先是问题的提出:

  很多人会觉得,既然C++沿用了C的指针这么强大的东西了,为什么还要iterator这么一群类来工作呢?

  我们知道,在我们编写模板的时候,对于使用了iterator做为参数的函数,往往该函数对于iterator有些特定的操作.比如下列2个函数

template

Pfind(Pstart,Pbeyond,constT&x)

{

while(start!

=beyond&&*start!

=x)

++start;

returnstart;

}

template

voidreverse(Pstart,Pbeyond)

{

while(start!

=beyond){

--beyond;

if(start!

=beyond){

Tt=*start;

*start=*beyond;

*beyond=t;

++start;

}

}

}

  我们可以看到,这两个函数都对模板参数P做了一定要求,在find中,我们要求P必须允许!

=,++和*(P)这三个运算符的操作,而对于reverse函数来说,其要求更多,运算符++,--,*(),!

=都必须支持.问题就这么出来了,我们怎么让所有人都遵守这一要求呢?

或者说,后续采用这个模板的使用者怎么能在不知道实现细节的情况下了解并遵守这些要求呢?

显然,我们需要一个分类方法来知道如何界定不同种类的迭代器.

  不过这里还是没有解释一个疑惑,即这两个函数改成T的指针也能完成的很好,我们还要iterator做什么?

  答案很简单,不是所有数据结构都是线性的!

对于一个链表来说,如果要实现上述的功能,我们必须重新写一个几乎一样仅仅是把++start变成start=start->next的函数.显然,这样重复的事情我们是不希望碰到的,尤其如果函数还非常大的情况下.而这时候,iterator的作用就出来了.对于链表,我们的链表的iterator(list_iterator)仅仅只要把operator++()重写成operator++(){now=now->next;},就能正常的使用find函数了.完全不需要重新编写.这就减少了我们算法的编写量.

  现在,既然我们知道了iterator的好处了之后,接下来我们就想知道之前提到的分类法是怎么回事了.经常听说输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机存取迭代器是怎么回事.

  迭代器的分类:

  1.输入迭代器(inputiterator)

inputiterator就像其名字所说的,工作的就像输入流一样.我们必须能

∙取出其所指向的值

∙访问下一个元素

∙判断是否到达了最后一个元素

∙可以复制

  因此其支持的操作符有*p,++p,p++,p!

=q,p==q这五个.凡是支持这五个操作的类都可以称作是输入迭代器.当然指针是符合的.

  2.输出迭代器(outputiterator)

   outputiterator工作方式类似输出流,我们能对其指向的序列进行写操作,其与inputiterator不相同的就是*p所返回的值允许修改,而不一定要读取,而input只允许读取,不允许修改.

   支持的操作和上头一样,支持的操作符也是*p,++p,p++,p!

=q,p==q.

  使用Inputiterator和outputiterator的例子:

 

1template

2voidcopy(Instart,Inbeyond,Outresult)

3{

4while(start!

=beyond){

5*result=*start;//result是输出迭代器,其*result返回的值允许修改

6++result;

7++start;

8}

9}

10

11//简写

12template

13voidcopy(Instart,Inbeyond,Outresult)

14{

15while(start!

=beyond)

16*result++=*start++;//这个应该能看懂的...

17}

  3.前向迭代器(forwarditerator)

   前向迭代器就像是输入和输出迭代器的结合体,其*p既可以访问元素,也可以修改元素.因此支持的操作也是相同的.

  4.双向迭代器(bidirectionaliterator)

   双向迭代器在前向迭代器上更近一步,其要求该种迭代器支持operator--,因此其支持的操作有*p,++p,p++,p!

=q,p==q,--p,p--

  5.随机存取迭代器(randomaccessiterator)

    即如其名字所显示的一样,其在双向迭代器的功能上,允许随机访问序列的任意值.显然,指针就是这样的一个迭代器.

    对于随机存取迭代器来说,其要求高了很多:

∙可以判断是否到结尾(a==bora!

=b)

∙可以双向递增或递减(--aor++a)

∙可以比较大小(abora>=b...etc)

∙支持算术运算(a+n)

∙支持随机访问(a[n])

∙支持复合运算(a+=n)

  结构图:

  上面即为我们所讨论到的几个迭代器的层次.看起来很像继承是麽?

  但是实际上在STL中,这些并不是通过继承关联的.这些只不过是一些符合条件的集合.这样的好处是:

少去了一个特殊的基类(inputiterator),其次,使得内建类型指针可以成为iterator.

其实只要明白iterator是满足某些特别的功能的集合(包括类和内建类型),就不会觉得是继承了.

  

  Iterator使用:

  一个ostream_iteartor的例子:

  

1#include

2

3usingnamespacestd;

4

5template

6classOstream_iterator{

7public:

8Ostream_iterator(ostream&os,constchar*s):

9strm(&os),str(s){}

10Ostream_iterator&operator++(){return*this;}

11Ostream_iterator&operator++(int){return*this;}

12Ostream_iterator&operator*(){return*this;}

13Ostream_iterator&operator=(constT&t)

14{

15*strm<

16return*this;

17}

18

19private:

20ostream*strm;

21constchar*str;

22};

23

24template

25OutCopy(Instart,Inbeyond,Outdest)

26{

27while(start!

=beyond)

28*dest++=*start++;

29returndest;

30}

31

32intmain()

33{

34Ostream_iteratoroi(cout,"\n");

35

36inta[10];

37for(inti=0;i!

=10;++i)

38a[i]=i+1;

39Copy(a,a+10,oi);

40

41return0;

42}

  在这个例子中,我们简单的构造了一个ostream_iterator,并且使用了copy来输出..这个ostream_iterator和STL差别还是很大,不过功能上已经差不多了.我想读者们应该也能看懂代码吧,所以就不用多做什么解释了.

  第二个例子中,我们讲构造一个istream_iterator.

  对于一个istream_iterator来说,我们必须要存的是Tbuffer和istream*strm.不过由于istream的特殊性,我们必须知道buffer是否满,以及是否到达尾部,因此,我们的结构是这样的

1template

2classIstream_Iterator{

3

4private:

5istream*strm;

6Tbuffer;

7intfull;

8inteof;

9};

  对于构造函数来说,因为有eof的存在,因此,我们自然想到,我们的默认构造函数就应该把eof设为1.而对于有istream的iterator,都应该为0.

  

1Istream_iterator(istream&is):

2strm(&is),full(0),eof(0){}

3Istream_iterator():

strm(0),full(0),eof

(1){}

  对于我们的版本的istream_iterator来说,我们需要完成的功能有

∙进行取值操作(dereference),既*(p)

∙自增操作

∙比较

  自增操作:

  我们知道,在istream中,一旦获取到一个值了之后,我们将不在访问这个值.我们的iterator也要符合这样的要求,因此,我们通过full来控制.

1Istream_iterator&operator++(){

2full=0;

3return*this;

4}

5Istream_iteratoroperator++(int){

6Istream_iteratorr=*this;

7full=0;

8returnr;

9}

  Dereference:

  对于解除引用操作来说,我们需要将流中缓存的值存入buffer内.同时,我们要判断读取是否到结尾,到了结尾则应当出错.

  在这里我们写了一个私有函数fill(),这个将在比较的时候用到

  

Toperator*(){

fill();

assert(eof);//我们断定不应该出现eof

returnbuffer;

}

voidfill(){

if(!

full&&!

eof){

if(*strm>>buffer)

full=1;

else

eof=1;

}

}

  比较:

  对于比较来说,只有两个istream_iterator都同一个对象或者都处于文件尾时,这两个istream_iterator才相等.因此这里其比较对象为istream_iterator而不是constistream_iterator

template

intoperator==(Istream_iterator&p,Istream_iterator&q)

{

if(p.eof&&q.eof)

return1;

if(!

p.eof&&!

q.eof)

return&p==&q;

p.fill();q.fill();

returnp.eof==q.eof;

}

最后的测试例子,读者可以把之前的copy和ostream_iterator拷下来

intmain()

{

Ostream_iteratoro(cout,"\t");

Istream_iteratori(cin);

Istream_iteratoreof;

Copy(i,eof,o);

return0;

}

结论:

  iterator是STL实现所有算法已经其通用型的基础.通过对iterator分类,使得算法的使用者在使用时不需要知道具体实现就可知道算法对于参数的要求,形成一个通用的体系.

对C++迭代器工作原理说明介绍

可以对C++迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素值赋值,因为C++迭代器对所有的容器都适用,现代C++程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的vector类型也这样。

标准库为每一种标准容器(包括vector)定义了一种迭代器类型。

迭代器类型提供了比下标操作更一般化的方法:

所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。

1.容器的iterator类型

每种容器类型都定义了自己的迭代器类型,如vector:

vector:

:

iteratoriter;这条语句定义了一个名为iter的变量,它的数据类型是由vector定义的iterator类型。

每个标准库容器类型都定义了一个名为iterator的成员,这里的iterator与迭代器实际类型的含义相同。

术语:

迭代器和C++迭代器类型程序员首次遇到有关迭代器的术语时可能会困惑不解,产生困惑的原因之一是由于本书中同一个术语iterator表示两个不同的事物。

一般性提及的是迭代器的概念;而特别提及的则是由容器定义的具体的iterator类型,如vector。

重点要理解的是,定义了许多用作迭代器的类型,这些类型在概念上是相关的。

若一种类型支持一组确定的行为(这些行为允许程序员遍历容器内的元素,并允许程序员访问这些元素值)。

我们就称这种类型为迭代器。

不同的容器类定义了自己的iterator类型,用于访问容器内的元素。

换句话说,每个容器定义了一种名为iterator的类型,而这种类型支持(概念上的)迭代器的各种行为。

2.begin和end操作

每种容器都定义了一对命名为begin和end的函数,用于返回迭代器。

如果容器中有元素的话,由begin返回的迭代器指向第一个元素把iter初始化为由名为begin的vector操作返回的值。

假设vector不空,初始化后,iter即指该元素为ivec[0]。

由end操作返回的迭代器指向vector的“末端元素的下一个”。

通常称为超出末端迭代器(off-the-enditerator),表明它指向了一个不存在的元素。

如果vector为空,begin返回的迭代器与end返回的迭代器相同。

由end操作返回的迭代器并不指向vector中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完vector中所有元素。

3.C++迭代器的自增和解引用运算

C++迭代器类型定义了一些操作来获取迭代器所指向的元素,并允许程序员将迭代器从一个元素移动到另一个元素。

迭代器类型可使用解引用操作符(*操作符)来访问迭代器所指向r元素解引用操作符返回迭代器当前所指向的元素。

假设iter指向vector对象ivec的第一个元素,那么*iter和ivec[0]就是指向同一个元素。

上面这个语句的效果就是把这个元素的值赋为0。

迭代器使用自增操作符(1.4.1节)向前移动迭代器指向容器中下一个元素。

从逻辑上说,迭代器的自增操作和int型对象的自增操作类似。

对int对象来说,操作结果就是把int型值“加1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。

因此,如果iter指向第一个元素,则++iter指向第二个元素。

由于end操作返回的C++迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

4.C++迭代器的其他运算

另一对可执行于迭代器的操作就是比较:

用==或!

=操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。

C++STL之迭代器

迭代器除了在STL中遍历序列对象外,还有其他更多的迭代器被iterator所定义。

iterator头文件定义迭代器的几个模板将数据从源传到目的地。

流迭代器(streamiterator)作为指向输入或输出流的指针,它们可以用来在流和任何使用迭代器的源或目的地之间传输数据,如算法。

插入迭代器(inserteriterator)可以将数据传输给一个基本的序列容器。

Iterator头文件定义了两个流迭代器模板,其中istream_iterator用于输入流,ostream_iterator用于输出流,T是要从流中提取的或者写入流中的对象类型。

头文件还定义了三个插入模板:

inserter、back_inserter和front_inserter,其中T是在其中插入数据的序列容器的类型。

输入流迭代器

创建输入流迭代器:

istream_iteratorinput(cin);

此代码创建了一个istream_iterator类型的迭代器input,可以指向流中int类型的对象。

这个构造函数的实指定与迭代器相关的实际流,因此它是一个可以从标准输入流cin中读取整数的迭代器。

默认的istream_iterator构造函数创建一个end-of-stream迭代器。

它等价于通过调用容器的end()函数获得容器的end迭代器。

下面代码说明了如何创建cin的end-of-stream迭代器来补充input迭代器:

istream_iteratorinputEnd;

现在有了一对迭代器,定义cin中一个int类型的值的序列,可以用这些迭代器将cin中的值加载到vector中。

vectornumbers;

cout<<”inputnumbers,endbyzero”<

istream_iteratorinput(cin),inputEnd;

while(input!

=inputEnd)

{

numbers.push_back(*input++);

}

可以看出,只要不是cin的end-of-stream,就会继续执行while输入数据,那么怎样才会产生end-of-stream的条件了,可以输入Ctrl+Z或输入一个无效的字符(如字母),就会产生end-of-stream条件。

当然,不只限于只能使用输入流迭代器作为循环的控制变量。

它们可以向算法传递数据,如numeric头文件中的accumulate()。

cout<<”inputnumbersseparatedbyspacesandalettertoend”<

istream_iteratorinput(cin),inputEnd;

cout<<”thesumofyouinputednumbersis“

<

sstream头文件定义basic_istringstream类型,这个类型定义可以访问流缓冲区中的数据的对象类型,如string对象。

这个头文件还将类型istringstream定义为basic_istringstream,它将是char类型的字符流。

可以从string对象中构造一个istringstream对象,这意味着从string对象中读取数据,就像从cin中读取数据一样。

因为istringstream对象是一个流,所以可将它传递给一个输入迭代器构造函数,并用该迭代器访问底层流缓冲区中的数据。

如:

stringdata[]={“1.212.63.6985.37.1”};

Istringstreaminput(data);

Istream_iteratorbegin(input),end;

cout<<”thesumofyouinputednumbersis“

<

由于从string对象data中创建istringstream对象,因此可以像流一样从data中读取数据。

此处accumulate()的第三个参数应为0.0,因为第三个参数确定结果的类型,此处保证返回double类型的值。

实例:

#include

#include

#include

#include

#include

usingstd:

:

cout;

usingstd:

:

cin;

usingstd:

:

endl;

usingstd:

:

string;

intmain()

{

std:

:

mapwords;//Maptostorewordsandwordcounts

cout<<"EntersometextandpressEnterfollowedbyCtrl+ZthenEntertoend:

"

<

std:

:

istream_iteratorbegin(cin);//Streamiterator

std:

:

istream_iteratorend;//Endstreamiterator

while(begin!

=end)//Iterateoverwordsinthestream

words[*begin++]++;//Incrementandstoreawordcount

//Outputthewordsandtheircounts

cout<

"<

for(autoiter=words.begin();iter!

=words.end();++iter)

cout<

:

setw(5)<second<<""<first<

return0;

}

插入迭代器

插入迭代器(inserteriterator)是一个可以访问序列容器vector、deque

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 韩语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1