📄 chapter06.html
字号:
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Nojump.cpp</font>
<font color=#009900>// Can't jump past constructors</font>
<font color=#0000ff>class</font> X {
<font color=#0000ff>public</font>:
X();
};
X::X() {}
<font color=#0000ff>void</font> f(<font color=#0000ff>int</font> i) {
<font color=#0000ff>if</font>(i < 10) {
<font color=#009900>//! goto jump1; // Error: goto bypasses init</font>
}
X x1; <font color=#009900>// Constructor called here</font>
jump1:
<font color=#0000ff>switch</font>(i) {
<font color=#0000ff>case</font> 1 :
X x2; <font color=#009900>// Constructor called here</font>
<font color=#0000ff>break</font>;
<font color=#009900>//! case 2 : // Error: case bypasses init</font>
X x3; <font color=#009900>// Constructor called here</font>
<font color=#0000ff>break</font>;
}
}
<font color=#0000ff>int</font> main() {
f(9);
f(11);
}<font color=#009900>///:~ </font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the code above, both the <B>goto</B>
and the <B>switch</B> can potentially jump past the sequence point where a
constructor is called. That object will then be in scope even if the constructor
hasn’t been called, so the compiler gives an error message. This once
again guarantees <A NAME="Index1338"></A><A NAME="Index1339"></A>that an object
cannot be created unless it is also initialized.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All the storage allocation discussed here
happens, of course, on the stack<A NAME="Index1340"></A>. The storage is
allocated by the compiler by moving the stack pointer “down” (a
relative term, which may indicate an increase or decrease of the actual stack
pointer <A NAME="Index1341"></A>value, depending on your machine). Objects can
also be allocated on the heap using <B>new</B>, which is something we’ll
explore further in Chapter
13.</FONT><A NAME="_Toc312373858"></A><A NAME="_Toc472654858"></A><BR></P></DIV>
<A NAME="Heading230"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Stash with constructors and destructors</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The examples from previous chapters have
obvious functions that map to constructors and destructors:
<B>initialize( )</B> and <B>cleanup( )</B>. Here’s the
<B>Stash</B> header using constructors and destructors:
<A NAME="Index1342"></A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Stash2.h</font>
<font color=#009900>// With constructors & destructors</font>
#ifndef STASH2_H
#define STASH2_H
<font color=#0000ff>class</font> Stash {
<font color=#0000ff>int</font> size; <font color=#009900>// Size of each space</font>
<font color=#0000ff>int</font> quantity; <font color=#009900>// Number of storage spaces</font>
<font color=#0000ff>int</font> next; <font color=#009900>// Next empty space</font>
<font color=#009900>// Dynamically allocated array of bytes:</font>
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* storage;
<font color=#0000ff>void</font> inflate(<font color=#0000ff>int</font> increase);
<font color=#0000ff>public</font>:
Stash(<font color=#0000ff>int</font> size);
~Stash();
<font color=#0000ff>int</font> add(<font color=#0000ff>void</font>* element);
<font color=#0000ff>void</font>* fetch(<font color=#0000ff>int</font> index);
<font color=#0000ff>int</font> count();
};
#endif <font color=#009900>// STASH2_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The only member function definitions that
are changed are <B>initialize( )</B> and <B>cleanup( )</B>, which have
been replaced with a constructor and destructor:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Stash2.cpp {O}</font>
<font color=#009900>// Constructors & destructors</font>
#include <font color=#004488>"Stash2.h"</font>
#include <font color=#004488>"../require.h"</font>
#include <iostream>
#include <cassert>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>const</font> <font color=#0000ff>int</font> increment = 100;
Stash::Stash(<font color=#0000ff>int</font> sz) {
size = sz;
quantity = 0;
storage = 0;
next = 0;
}
<font color=#0000ff>int</font> Stash::add(<font color=#0000ff>void</font>* element) {
<font color=#0000ff>if</font>(next >= quantity) <font color=#009900>// Enough space left?</font>
inflate(increment);
<font color=#009900>// Copy element into storage,</font>
<font color=#009900>// starting at next empty space:</font>
<font color=#0000ff>int</font> startBytes = next * size;
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* e = (<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>*)element;
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < size; i++)
storage[startBytes + i] = e[i];
next++;
<font color=#0000ff>return</font>(next - 1); <font color=#009900>// Index number</font>
}
<font color=#0000ff>void</font>* Stash::fetch(<font color=#0000ff>int</font> index) {
require(0 <= index, <font color=#004488>"Stash::fetch (-)index"</font>);
<font color=#0000ff>if</font>(index >= next)
<font color=#0000ff>return</font> 0; <font color=#009900>// To indicate the end</font>
<font color=#009900>// Produce pointer to desired element:</font>
<font color=#0000ff>return</font> &(storage[index * size]);
}
<font color=#0000ff>int</font> Stash::count() {
<font color=#0000ff>return</font> next; <font color=#009900>// Number of elements in CStash</font>
}
<font color=#0000ff>void</font> Stash::inflate(<font color=#0000ff>int</font> increase) {
require(increase > 0,
<font color=#004488>"Stash::inflate zero or negative increase"</font>);
<font color=#0000ff>int</font> newQuantity = quantity + increase;
<font color=#0000ff>int</font> newBytes = newQuantity * size;
<font color=#0000ff>int</font> oldBytes = quantity * size;
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* b = <font color=#0000ff>new</font> <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>[newBytes];
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < oldBytes; i++)
b[i] = storage[i]; <font color=#009900>// Copy old to new</font>
<font color=#0000ff>delete</font> [](storage); <font color=#009900>// Old storage</font>
storage = b; <font color=#009900>// Point to new memory</font>
quantity = newQuantity;
}
Stash::~Stash() {
<font color=#0000ff>if</font>(storage != 0) {
cout << <font color=#004488>"freeing storage"</font> << endl;
<font color=#0000ff>delete</font> []storage;
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that the <B>require.h</B>
functions are being used to watch for programmer errors, instead of
<B>assert( )</B>. The output of a failed <B>assert( )</B> is not as
useful as that of the <B>require.h</B> functions (which will be shown later in
the book).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because <B>inflate( )</B> is
private, the only way a <B>require( )</B> could fail is if one of the other
member functions accidentally passed an incorrect value to
<B>inflate( )</B>. If you are certain this can’t happen, you could
consider removing the <B>require( )</B>, but you might keep in mind that
until the class is stable, there’s always the possibility that new code
might be added to the class that could cause errors. The cost of the
<B>require( )</B> is low (and could be automatically removed using the
preprocessor) and the value of code robustness is high.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice in the following test program how
the definitions for <B>Stash</B> objects appear right before they are needed,
and how the initialization appears as part of the definition, in the constructor
argument list:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Stash2Test.cpp</font>
<font color=#009900>//{L} Stash2</font>
<font color=#009900>// Constructors & destructors</font>
#include <font color=#004488>"Stash2.h"</font>
#include <font color=#004488>"../require.h"</font>
#include <fstream>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
Stash intStash(<font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>));
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 100; i++)
intStash.add(&i);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j < intStash.count(); j++)
cout << <font color=#004488>"intStash.fetch("</font> << j << <font color=#004488>") = "</font>
<< *(<font color=#0000ff>int</font>*)intStash.fetch(j)
<< endl;
<font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize = 80;
Stash stringStash(<font color=#0000ff>sizeof</font>(<font color=#0000ff>char</font>) * bufsize);
ifstream in(<font color=#004488>"Stash2Test.cpp"</font>);
assure(in, <font color=#004488>" Stash2Test.cpp"</font>);
string line;
<font color=#0000ff>while</font>(getline(in, line))
stringStash.add((<font color=#0000ff>char</font>*)line.c_str());
<font color=#0000ff>int</font> k = 0;
<font color=#0000ff>char</font>* cp;
<font color=#0000ff>while</font>((cp = (<font color=#0000ff>char</font>*)stringStash.fetch(k++))!=0)
cout << <font color=#004488>"stringStash.fetch("</font> << k << <font color=#004488>") = "</font>
<< cp << endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Also notice how the
<B>cleanup( )</B> calls have been eliminated, but the
<A NAME="Index1343"></A>destructors are still automatically
<A NAME="Index1344"></A>called when <B>intStash</B> and <B>stringStash</B> go
out of scope.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One thing to be aware of in the
<B>Stash</B> examples: I’m being very careful to use only built-in types;
that is, those without destructors. If you were to try to copy class objects
into the <B>Stash</B>, you’d run into all kinds of problems and it
wouldn’t work right. The Standard C++ Library can actually make correct
copies of objects into its containers, but this is a rather messy and
complicated process. In the following <B>Stack</B> example, you’ll see
that pointers are used to sidestep this issue, and in a later chapter the
<B>Stash</B> will be converted so that it uses
pointers.</FONT><A NAME="_Toc312373859"></A><A NAME="_Toc472654859"></A><BR></P></DIV>
<A NAME="Heading231"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Stack with constructors & destructors</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Reimplementing the linked list
<A NAME="Index1345"></A><A NAME="Index1346"></A>(inside <B>Stack</B>)<B>
</B>with constructors and destructors shows how neatly constructors and
destructors work with <B>new </B>and <B>delete</B>. Here’s the modified
header file: <A NAME="Index1347"></A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Stack3.h</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -