chap04.htm.kbk

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

KBK
1,135 行
字号
object to hold the value before the increment, so it can be returned (see the
operator overloading chapter for more details of this). Producing the actual
value is a straightforward <B>operator*</B>. The only other functions that must
be defined for an output iterator are the <B>operator==</B> and
<B>operator!=</B> to indicate whether the <B>TokenIterator</B> has reached the
end of its input. You can see that the argument for <B>operator== </B>is ignored
&#8211; it only cares about whether it has reached its internal <B>last</B>
iterator. Notice that <B>operator!=</B> is defined in terms of
<B>operator==</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A good test of <B>TokenIterator</B>
includes a number of different sources of input characters including a
<B>streambuf_iterator</B>, a <B>char*</B>, and a
<B>deque&lt;char&gt;::iterator</B>. Finally, the original <B>Wordlist.cpp</B>
problem is solved:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:TokenIteratorTest.cpp</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-g++295}</font>
<font color=#009900>//{-g++3}</font>
#include <font color=#004488>"TokenIterator.h"</font>
#include <font color=#004488>"..</font><font color=#004488>/require.h"</font>
#include &lt;fstream&gt;
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;deque&gt;
#include &lt;set&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main(<font color=#0000ff>int</font> argc, <font color=#0000ff>char</font>* argv[]) {
  <font color=#0000ff>char</font>* fname = <font color=#004488>"TokenIteratorTest.cpp"</font>;
  <font color=#0000ff>if</font>(argc &gt; 1) fname = argv[1];
  ifstream in(fname);
  assure(in, fname);
  ostream_iterator&lt;string&gt; out(cout, <font color=#004488>"\n"</font>);
  <font color=#0000ff>typedef</font> istreambuf_iterator&lt;<font color=#0000ff>char</font>&gt; IsbIt;
  IsbIt begin(in), isbEnd;
  Delimiters 
    delimiters(<font color=#004488>" \t\n~;()\"</font>&lt;&gt;:{}[]+-=&amp;*#.,/\\");
  TokenIterator&lt;IsbIt, Delimiters&gt; 
    wordIter(begin, isbEnd, delimiters),
    end;
  vector&lt;string&gt; wordlist;
  copy(wordIter, end, back_inserter(wordlist));
  <font color=#009900>// Output results:</font>
  copy(wordlist.begin(), wordlist.end(), out);
  *out++ = <font color=#004488>"-----------------------------------"</font>;
  <font color=#009900>// Use a char array as the source:</font>
  <font color=#0000ff>char</font>* cp = 
    <font color=#004488>"typedef std::istreambuf_iterator&lt;char&gt; It"</font>;
  TokenIterator&lt;<font color=#0000ff>char</font>*, Delimiters&gt;
    charIter(cp, cp + strlen(cp), delimiters),
    end2;
  vector&lt;string&gt; wordlist2;
  copy(charIter, end2, back_inserter(wordlist2));
  copy(wordlist2.begin(), wordlist2.end(), out);
  *out++ = <font color=#004488>"-----------------------------------"</font>;
  <font color=#009900>// Use a deque&lt;char&gt; as the source:</font>
  ifstream in2(<font color=#004488>"TokenIteratorTest.cpp"</font>);
  deque&lt;<font color=#0000ff>char</font>&gt; dc;
  copy(IsbIt(in2), IsbIt(), back_inserter(dc));
  TokenIterator&lt;deque&lt;<font color=#0000ff>char</font>&gt;::iterator,Delimiters&gt;
    dcIter(dc.begin(), dc.end(), delimiters),
    end3;
  vector&lt;string&gt; wordlist3;
  copy(dcIter, end3, back_inserter(wordlist3));
  copy(wordlist3.begin(), wordlist3.end(), out);
  *out++ = <font color=#004488>"-----------------------------------"</font>;
  <font color=#009900>// Reproduce the Wordlist.cpp example:</font>
  ifstream in3(<font color=#004488>"TokenIteratorTest.cpp"</font>);
  TokenIterator&lt;IsbIt, Delimiters&gt;
    wordIter2(IsbIt(in3), isbEnd, delimiters);
  set&lt;string&gt; wordlist4;
  <font color=#0000ff>while</font>(wordIter2 != end)
    wordlist4.insert(*wordIter2++);
  copy(wordlist4.begin(), wordlist4.end(), out);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When using an <B>istreambuf_iterator</B>,
you create one to attach to the <B>istream</B> object, and one with the default
constructor as the past-the-end marker. Both of these are used to create the
<B>TokenIterator</B> that will actually produce the tokens; the default
constructor produces the faux <B>TokenIterator</B> past-the-end sentinel (this
is just a placeholder, and as mentioned previously is actually ignored). The<B>
TokenIterator</B> produces <B>string</B>s that are inserted into a container
which must, naturally, be a container of <B>string</B> &#8211; here a
<B>vector&lt;string&gt;</B> is used in all cases except the last (you could also
concatenate the results onto a <B>string</B>). Other than that, a
<B>TokenIterator</B> works like any other input
iterator.</FONT><A NAME="_Toc519042038"></A><BR></P></DIV>
<A NAME="Heading226"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
stack</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>stack</B>, along with the
<B>queue</B> and <B>priority_queue</B>, are classified as <I>adapters</I>, which
means they are implemented using one of the basic sequence containers:
<B>vector</B>, <B>list</B> or <B>deque</B>. This, in my opinion, is an
unfortunate case of confusing what something does with the details of its
underlying implementation &#8211; the fact that these are called
&#8220;adapters&#8221; is of primary value only to the creator of the library.
When you use them, you generally don&#8217;t care that they&#8217;re adapters,
but instead that they solve your problem. Admittedly there are times when
it&#8217;s useful to know that you can choose an alternate implementation or
build an adapter from an existing container object, but that&#8217;s generally
one level removed from the adapter&#8217;s behavior. So, while you may see it
emphasized elsewhere that a particular container is an adapter, I shall only
point out that fact when it&#8217;s useful. Note that each type of adapter has a
default container that it&#8217;s built upon, and this default is the most
sensible implementation, so in most cases you won&#8217;t need to concern
yourself with the underlying implementation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The following example shows
<B>stack&lt;string&gt;</B> implemented in the three possible ways: the default
(which uses <B>deque</B>), with a <B>vector</B> and with a
<B>list</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:Stack1.cpp</font>
<font color=#009900>// Demonstrates the STL stack</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;stack&gt;
#include &lt;list&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#009900>// Default: deque&lt;string&gt;:</font>
<font color=#0000ff>typedef</font> stack&lt;string&gt; Stack1;
<font color=#009900>// Use a vector&lt;string&gt;:</font>
<font color=#0000ff>typedef</font> stack&lt;string, vector&lt;string&gt; &gt; Stack2;
<font color=#009900>// Use a list&lt;string&gt;:</font>
<font color=#0000ff>typedef</font> stack&lt;string, list&lt;string&gt; &gt; Stack3;

<font color=#0000ff>int</font> main() {
  ifstream in(<font color=#004488>"Stack1.cpp"</font>);
  Stack1 textlines; <font color=#009900>// Try the different versions</font>
  <font color=#009900>// Read file and store lines in the stack:</font>
  string line;
  <font color=#0000ff>while</font>(getline(in, line)) 
    textlines.push(line + <font color=#004488>"\n"</font>);
  <font color=#009900>// Print lines from the stack and pop them:</font>
  <font color=#0000ff>while</font>(!textlines.empty()) {
    cout &lt;&lt; textlines.top();
    textlines.pop();
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>top(&#160;)</B> and
<B>pop(&#160;)</B> operations will probably seem non-intuitive if you&#8217;ve
used other <B>stack</B> classes. When you call <B>pop(&#160;)</B> it returns
void rather than the top element that you might have expected. If you want the
top element, you get a reference to it with <B>top(&#160;)</B>. It turns out
this is more efficient, since a traditional <B>pop(&#160;)</B> would have to
return a value rather than a reference, and thus invoke the copy-constructor.
When you&#8217;re using a <B>stack</B> (or a <B>priority_queue</B>, described
later) you can efficiently refer to <B>top(&#160;)</B> as many times as you
want, then discard the top element explicitly using <B>pop(&#160;)</B> (perhaps
if some other term than the familiar &#8220;pop&#8221; had been used, this would
have been a bit clearer).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>stack</B> template has a very
simple interface, essentially the member functions you see above. It
doesn&#8217;t have sophisticated forms of initialization or access, but if you
need that you can use the underlying container that the <B>stack</B> is
implemented upon. For example, suppose you have a function that expects a
<B>stack</B> interface but in the rest of your program you need the objects
stored in a <B>list</B>. The following program stores each line of a file along
with the leading number of spaces in that line (you might imagine it as a
starting point for performing some kinds of source-code
reformatting):</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C07:Stack2.cpp</font>
<font color=#009900>// Converting a list to a stack</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-msc}</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;stack&gt;
#include &lt;list&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#009900>// Expects a stack:</font>
<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> Stk&gt;
<font color=#0000ff>void</font> stackOut(Stk&amp; s, ostream&amp; os = cout) {
  <font color=#0000ff>while</font>(!s.empty()) {
    os &lt;&lt; s.top() &lt;&lt; <font color=#004488>"\n"</font>;
    s.pop();
  }
}

<font color=#0000ff>class</font> Line {
  string line; <font color=#009900>// Without leading spaces</font>
  <font color=#0000ff>int</font> lspaces; <font color=#009900>// Number of leading spaces</font>
<font color=#0000ff>public</font>:
  Line(string s) : line(s) {
    lspaces = line.find_first_not_of(' ');
    <font color=#0000ff>if</font>(lspaces == string::npos)
      lspaces = 0;
    line = line.substr(lspaces);
  }
  <font color=#0000ff>friend</font> ostream&amp; 
  <font color=#0000ff>operator</font>&lt;&lt;(ostream&amp; os, <font color=#0000ff>const</font> Line&amp; l) {
    <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; l.lspaces; i++)
      os &lt;&lt; ' ';
    <font color=#0000ff>return</font> os &lt;&lt; l.line;
  }
  <font color=#009900>// Other functions here...</font>
};

<font color=#0000ff>int</font> main() {
  ifstream in(<font color=#004488>"Stack2.cpp"</font>);
  list&lt;Line&gt; lines;
  <font color=#009900>// Read file and store lines in the list:</font>
  string s;
  <font color=#0000ff>while</font>(getline(in, s)) 
    lines.push_front(s);
  <font color=#009900>// Turn the list into a stack for printing:</font>
  stack&lt;Line, list&lt;Line&gt; &gt; stk(lines);
  stackOut(stk);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The function that requires the
<B>stack</B> interface just sends each <B>top(&#160;)</B> object to an
<B>ostream</B> and then removes it by calling <B>pop(&#160;)</B>. The
<B>Line</B> class determines the number of leading spaces, then stores the
contents of the line <I>without</I> the leading spaces. The <B>ostream</B>
<B>operator&lt;&lt;</B> re-inserts the leading spaces so the line prints
properly, but you can easily change the number of spaces by changing the value
of <B>lspaces</B> (the member functions to do this are not shown
here).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, the input file is
read into a <B>list&lt;Line&gt;</B>, then a <B>stack</B> is wrapped around this

⌨️ 快捷键说明

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