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
– 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<char>::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 <fstream>
#include <iostream>
#include <vector>
#include <deque>
#include <set>
<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 > 1) fname = argv[1];
ifstream in(fname);
assure(in, fname);
ostream_iterator<string> out(cout, <font color=#004488>"\n"</font>);
<font color=#0000ff>typedef</font> istreambuf_iterator<<font color=#0000ff>char</font>> IsbIt;
IsbIt begin(in), isbEnd;
Delimiters
delimiters(<font color=#004488>" \t\n~;()\"</font><>:{}[]+-=&*#.,/\\");
TokenIterator<IsbIt, Delimiters>
wordIter(begin, isbEnd, delimiters),
end;
vector<string> 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<char> It"</font>;
TokenIterator<<font color=#0000ff>char</font>*, Delimiters>
charIter(cp, cp + strlen(cp), delimiters),
end2;
vector<string> wordlist2;
copy(charIter, end2, back_inserter(wordlist2));
copy(wordlist2.begin(), wordlist2.end(), out);
*out++ = <font color=#004488>"-----------------------------------"</font>;
<font color=#009900>// Use a deque<char> as the source:</font>
ifstream in2(<font color=#004488>"TokenIteratorTest.cpp"</font>);
deque<<font color=#0000ff>char</font>> dc;
copy(IsbIt(in2), IsbIt(), back_inserter(dc));
TokenIterator<deque<<font color=#0000ff>char</font>>::iterator,Delimiters>
dcIter(dc.begin(), dc.end(), delimiters),
end3;
vector<string> 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<IsbIt, Delimiters>
wordIter2(IsbIt(in3), isbEnd, delimiters);
set<string> 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> – here a
<B>vector<string></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 – the fact that these are called
“adapters” is of primary value only to the creator of the library.
When you use them, you generally don’t care that they’re adapters,
but instead that they solve your problem. Admittedly there are times when
it’s useful to know that you can choose an alternate implementation or
build an adapter from an existing container object, but that’s generally
one level removed from the adapter’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’s useful. Note that each type of adapter has a
default container that it’s built upon, and this default is the most
sensible implementation, so in most cases you won’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<string></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 <iostream>
#include <fstream>
#include <stack>
#include <list>
#include <vector>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#009900>// Default: deque<string>:</font>
<font color=#0000ff>typedef</font> stack<string> Stack1;
<font color=#009900>// Use a vector<string>:</font>
<font color=#0000ff>typedef</font> stack<string, vector<string> > Stack2;
<font color=#009900>// Use a list<string>:</font>
<font color=#0000ff>typedef</font> stack<string, list<string> > 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 << textlines.top();
textlines.pop();
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>top( )</B> and
<B>pop( )</B> operations will probably seem non-intuitive if you’ve
used other <B>stack</B> classes. When you call <B>pop( )</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( )</B>. It turns out
this is more efficient, since a traditional <B>pop( )</B> would have to
return a value rather than a reference, and thus invoke the copy-constructor.
When you’re using a <B>stack</B> (or a <B>priority_queue</B>, described
later) you can efficiently refer to <B>top( )</B> as many times as you
want, then discard the top element explicitly using <B>pop( )</B> (perhaps
if some other term than the familiar “pop” 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’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 <iostream>
#include <fstream>
#include <stack>
#include <list>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#009900>// Expects a stack:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> Stk>
<font color=#0000ff>void</font> stackOut(Stk& s, ostream& os = cout) {
<font color=#0000ff>while</font>(!s.empty()) {
os << s.top() << <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&
<font color=#0000ff>operator</font><<(ostream& os, <font color=#0000ff>const</font> Line& l) {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < l.lspaces; i++)
os << ' ';
<font color=#0000ff>return</font> os << l.line;
}
<font color=#009900>// Other functions here...</font>
};
<font color=#0000ff>int</font> main() {
ifstream in(<font color=#004488>"Stack2.cpp"</font>);
list<Line> 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<Line, list<Line> > 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( )</B> object to an
<B>ostream</B> and then removes it by calling <B>pop( )</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<<</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( )</B>, the input file is
read into a <B>list<Line></B>, then a <B>stack</B> is wrapped around this
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?