chap02.htm.kbk

来自「c++设计思想」· KBK 代码 · 共 1,047 行 · 第 1/5 页

KBK
1,047
字号
algorithm requires determines how flexible the algorithm will be. If you only
require the most primitive iterator category (input or output) then your
algorithm will work with <I>everything</I> (<B>copy(&#160;)</B> is an example of
this).</FONT><A NAME="_Toc519042019"></A></OL><A NAME="Heading204"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Predefined iterators</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The STL has a predefined set of iterator
classes that can be quite handy. For example, you&#8217;ve already seen
<B>reverse_iterator</B> (produced by calling <B>rbegin(&#160;)</B> and
<B>rend(&#160;)</B> for all the basic containers).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <I>insertion iterators</I> are
necessary because some of the STL algorithms &#8211; <B>copy(&#160;)</B> for
example &#8211; use the assignment <B>operator=</B> in order to place objects in
the destination container. This is a problem when you&#8217;re using the
algorithm to <I>fill</I> the container rather than to overwrite items that are
already in the destination container. That is, when the space isn&#8217;t
already there. What the insert iterators do is change the implementation of the
<B>operator=</B> so that instead of doing an assignment, it calls a
&#8220;push&#8221; or &#8220;insert&#8221; function for that container, thus
causing it to allocate new space. The constructors for both
<B>back_insert_iterator</B> and <B>front_insert_iterator</B> take a basic
sequence container object (<B>vector</B>,<B> deque</B> or <B>list</B>) as their
argument and produce an iterator that calls <B>push_back(&#160;)</B> or
<B>push_front(&#160;)</B>, respectively, to perform assignment. The shorthand
functions <B>back_inserter(&#160;)</B> and <B>front_inserter(&#160;)</B> produce
the same objects with a little less typing. Since all the basic sequence
containers support <B>push_back(&#160;)</B>, you will probably find yourself
using <B>back_inserter(&#160;)</B> with some regularity.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>insert_iterator</B> allows you to
insert elements in the middle of the sequence, again replacing the meaning of
<B>operator=</B>, but this time with <B>insert(&#160;)</B> instead of one of the
&#8220;push&#8221; functions. The <B>insert(&#160;)</B> member function requires
an iterator indicating the place to insert before, so the <B>insert_iterator</B>
requires this iterator in addition to the container object. The shorthand
function <B>inserter(&#160;)</B> produces the same object.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The following example shows the use of
the different types of inserters:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:Inserters.cpp</font>
<font color=#009900>// Different types of iterator inserters</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;deque&gt;
#include &lt;list&gt;
#include &lt;iterator&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> a[] = { 1, 3, 5, 7, 11, 13, 17, 19, 23 };

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> Cont&gt;
<font color=#0000ff>void</font> frontInsertion(Cont&amp; ci) {
  copy(a, a + <font color=#0000ff>sizeof</font>(a)/<font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>), 
    front_inserter(ci));
  copy(ci.begin(), ci.end(),
    ostream_iterator&lt;<font color=#0000ff>int</font>&gt;(cout, <font color=#004488>" "</font>));
  cout &lt;&lt; endl;
}

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> Cont&gt;
<font color=#0000ff>void</font> backInsertion(Cont&amp; ci) {
  copy(a, a + <font color=#0000ff>sizeof</font>(a)/<font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>), 
    back_inserter(ci));
  copy(ci.begin(), ci.end(),
    ostream_iterator&lt;<font color=#0000ff>int</font>&gt;(cout, <font color=#004488>" "</font>));
  cout &lt;&lt; endl;
}

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> Cont&gt;
<font color=#0000ff>void</font> midInsertion(Cont&amp; ci) {
  <font color=#0000ff>typename</font> Cont::iterator it = ci.begin();
  it++; it++; it++;
  copy(a, a + <font color=#0000ff>sizeof</font>(a)/(<font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>) * 2),
    inserter(ci, it));
  copy(ci.begin(), ci.end(),
    ostream_iterator&lt;<font color=#0000ff>int</font>&gt;(cout, <font color=#004488>" "</font>));
  cout &lt;&lt; endl;
}

<font color=#0000ff>int</font> main() {
  deque&lt;<font color=#0000ff>int</font>&gt; di;
  list&lt;<font color=#0000ff>int</font>&gt;  li;
  vector&lt;<font color=#0000ff>int</font>&gt; vi;
  <font color=#009900>// Can't use a front_inserter() with vector</font>
  frontInsertion(di);
  frontInsertion(li);
  di.clear();
  li.clear();
  backInsertion(vi);
  backInsertion(di);
  backInsertion(li);
  midInsertion(vi);
  midInsertion(di);
  midInsertion(li);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since <B>vector</B> does not support
<B>push_front(&#160;)</B>, it cannot produce a <B>front_insertion_iterator</B>.
However, you can see that <B>vector</B> does support the other two types of
insertion (even though, as you shall see later, <B>insert(&#160;)</B> is not a
very efficient operation for <B>vector</B>).</FONT><BR></P></DIV>
<A NAME="Heading205"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H4 ALIGN="LEFT">
IO stream iterators</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You&#8217;ve already seen some use of the
<B>ostream_iterator</B> (an output iterator) in conjunction with
<B>copy(&#160;)</B> to place the contents of a container on an output stream.
There is a corresponding <B>istream_iterator</B> (an input iterator) which
allows you to &#8220;iterate&#8221; a set of objects of a specified type from an
input stream. An important difference between <B>ostream_iterator</B> and
<B>istream_iterator</B> comes from the fact that an output stream doesn&#8217;t
have any concept of an &#8220;end,&#8221; since you can always just keep writing
more elements. However, an input stream eventually terminates (for example, when
you reach the end of a file) so there needs to be a way to represent that. An
<B>istream_iterator</B> has two constructors, one that takes an <B>istream</B>
and produces the iterator you actually read from, and the other which is the
default constructor and produces an object which is the past-the-end sentinel.
In the following program this object is named <B>end</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:StreamIt.cpp</font>
<font color=#009900>// Iterators for istreams and ostreams</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-msc}</font>
#include <font color=#004488>"..</font><font color=#004488>/require.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main() {
  ifstream in(<font color=#004488>"StreamIt.cpp"</font>);
  assure(in, <font color=#004488>"StreamIt.cpp"</font>);
  istream_iterator&lt;string&gt; init(in), end;
  ostream_iterator&lt;string&gt; out(cout, <font color=#004488>"\n"</font>);
  vector&lt;string&gt; vs;
  copy(init, end, back_inserter(vs));
  copy(vs.begin(), vs.end(), out);
  *out++ = vs[0];
  *out++ = <font color=#004488>"That's all, folks!"</font>;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When <B>in</B> runs out of input (in this
case when the end of the file is reached) then <B>init</B> becomes equivalent to
<B>end</B> and the <B>copy(&#160;)</B> terminates.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because <B>out</B> is an
<B>ostream_iterator&lt;string&gt;</B>, you can simply assign any <B>string</B>
object to the dereferenced iterator using <B>operator=</B> and that
<B>string</B> will be placed on the output stream, as seen in the two
assignments to <B>out</B>. Because <B>out</B> is defined with a newline as its
second argument, these assignments also cause a newline to be inserted along
with each assignment.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">While it is possible to create an
<B>istream_iterator&lt;char&gt;</B> and <B>ostream_iterator&lt;char&gt;</B>,
these actually <I>parse </I>the input and thus will for example automatically
eat whitespace (spaces, tabs and newlines), which is not desirable if you want
to manipulate an exact representation of an <B>istream</B>. Instead, you can use
the special iterators <B>istreambuf_iterator</B> and <B>ostreambuf_iterator</B>,
which are designed strictly to move
characters</FONT><A NAME="fnB20" HREF="#fn20">[20]</A><FONT FACE="Georgia">.
Although these are templates, the only template arguments they will accept are
either <B>char</B> or <B>wchar_t</B> (for wide characters). The following
example allows you to compare the behavior of the stream iterators vs. the
streambuf iterators:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:StreambufIterator.cpp</font>
<font color=#009900>// istreambuf_iterator &amp; ostreambuf_iterator</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-g++295} </font>
#include <font color=#004488>"..</font><font color=#004488>/require.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;iterator&gt;
#include &lt;algorithm&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main() {
  ifstream in(<font color=#004488>"StreambufIterator.cpp"</font>);
  assure(in, <font color=#004488>"StreambufIterator.cpp"</font>);
  <font color=#009900>// Exact representation of stream:</font>
  istreambuf_iterator&lt;<font color=#0000ff>char</font>&gt; isb(in), end;
  ostreambuf_iterator&lt;<font color=#0000ff>char</font>&gt; osb(cout);
  <font color=#0000ff>while</font>(isb != end)
    *osb++ = *isb++; <font color=#009900>// Copy 'in' to cout</font>
  cout &lt;&lt; endl;
  ifstream in2(<font color=#004488>"StreambufIterator.cpp"</font>);
  <font color=#009900>// Strips white space:</font>
  istream_iterator&lt;<font color=#0000ff>char</font>&gt; is(in2), end2;
  ostream_iterator&lt;<font color=#0000ff>char</font>&gt; os(cout);
  <font color=#0000ff>while</font>(is != end2)
    *os++ = *is++;
  cout &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The stream iterators use the parsing
defined by <B>istream::operator&gt;&gt;</B>, which is probably
not</FONT><BR><FONT FACE="Georgia">what you want if you are parsing characters
directly &#8211; it&#8217;s fairly rare that you would want all the whitespace
stripped out of your character stream. You&#8217;ll virtually always want to use
a streambuf iterator when using characters and streams, rather than a stream
iterator. In addition, <B>istream::operator&gt;&gt;</B> adds significant
overhead for each operation, so it is only appropriate for higher-level
operations such as parsing floating-point
numbers.</FONT><A NAME="fnB21" HREF="#fn21">[21]</A><BR></P></DIV>
<A NAME="Heading206"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H4 ALIGN="LEFT">
Manipulating raw storage</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is a little more esoteric and is
generally used in the implementation of other Standard Library functions, but it
is nonetheless interesting. The <B>raw_storage_iterator</B> is defined in
<B>&lt;algorithm&gt;</B> and is an output iterator. It is provided to enable
algorithms to store their results into uninitialized memory. The interface is
quite simple: the constructor 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?