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( )</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’ve already seen
<B>reverse_iterator</B> (produced by calling <B>rbegin( )</B> and
<B>rend( )</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 – <B>copy( )</B> for
example – use the assignment <B>operator=</B> in order to place objects in
the destination container. This is a problem when you’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’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
“push” or “insert” 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( )</B> or
<B>push_front( )</B>, respectively, to perform assignment. The shorthand
functions <B>back_inserter( )</B> and <B>front_inserter( )</B> produce
the same objects with a little less typing. Since all the basic sequence
containers support <B>push_back( )</B>, you will probably find yourself
using <B>back_inserter( )</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( )</B> instead of one of the
“push” functions. The <B>insert( )</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( )</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 <iostream>
#include <vector>
#include <deque>
#include <list>
#include <iterator>
<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><<font color=#0000ff>class</font> Cont>
<font color=#0000ff>void</font> frontInsertion(Cont& 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<<font color=#0000ff>int</font>>(cout, <font color=#004488>" "</font>));
cout << endl;
}
<font color=#0000ff>template</font><<font color=#0000ff>class</font> Cont>
<font color=#0000ff>void</font> backInsertion(Cont& 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<<font color=#0000ff>int</font>>(cout, <font color=#004488>" "</font>));
cout << endl;
}
<font color=#0000ff>template</font><<font color=#0000ff>class</font> Cont>
<font color=#0000ff>void</font> midInsertion(Cont& 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<<font color=#0000ff>int</font>>(cout, <font color=#004488>" "</font>));
cout << endl;
}
<font color=#0000ff>int</font> main() {
deque<<font color=#0000ff>int</font>> di;
list<<font color=#0000ff>int</font>> li;
vector<<font color=#0000ff>int</font>> 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( )</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( )</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’ve already seen some use of the
<B>ostream_iterator</B> (an output iterator) in conjunction with
<B>copy( )</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 “iterate” 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’t
have any concept of an “end,” 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 <iostream>
#include <fstream>
#include <vector>
#include <string>
<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<string> init(in), end;
ostream_iterator<string> out(cout, <font color=#004488>"\n"</font>);
vector<string> 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( )</B> terminates.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because <B>out</B> is an
<B>ostream_iterator<string></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<char></B> and <B>ostream_iterator<char></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 & 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 <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
<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<<font color=#0000ff>char</font>> isb(in), end;
ostreambuf_iterator<<font color=#0000ff>char</font>> osb(cout);
<font color=#0000ff>while</font>(isb != end)
*osb++ = *isb++; <font color=#009900>// Copy 'in' to cout</font>
cout << endl;
ifstream in2(<font color=#004488>"StreambufIterator.cpp"</font>);
<font color=#009900>// Strips white space:</font>
istream_iterator<<font color=#0000ff>char</font>> is(in2), end2;
ostream_iterator<<font color=#0000ff>char</font>> os(cout);
<font color=#0000ff>while</font>(is != end2)
*os++ = *is++;
cout << 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>></B>, which is probably
not</FONT><BR><FONT FACE="Georgia">what you want if you are parsing characters
directly – it’s fairly rare that you would want all the whitespace
stripped out of your character stream. You’ll virtually always want to use
a streambuf iterator when using characters and streams, rather than a stream
iterator. In addition, <B>istream::operator>></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><algorithm></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 + -
显示快捷键?