⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter13.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<font color=#009900>// Test of pointer Stash</font>
#include <font color=#004488>"PStash.h"</font>
#include <font color=#004488>"../require.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main() {
  PStash intStash;
  <font color=#009900>// 'new' works with built-in types, too. Note</font>
  <font color=#009900>// the "pseudo-constructor" syntax:</font>
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; 25; i++)
    intStash.add(<font color=#0000ff>new</font> <font color=#0000ff>int</font>(i));
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j &lt; intStash.count(); j++)
    cout &lt;&lt; <font color=#004488>"intStash["</font> &lt;&lt; j &lt;&lt; <font color=#004488>"] = "</font>
         &lt;&lt; *(<font color=#0000ff>int</font>*)intStash[j] &lt;&lt; endl;
  <font color=#009900>// Clean up:</font>
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> k = 0; k &lt; intStash.count(); k++)
    <font color=#0000ff>delete</font> intStash.remove(k);
  ifstream in (<font color=#004488>"PStashTest.cpp"</font>);
  assure(in, <font color=#004488>"PStashTest.cpp"</font>);
  PStash stringStash;
  string line;
  <font color=#0000ff>while</font>(getline(in, line))
    stringStash.add(<font color=#0000ff>new</font> string(line));
  <font color=#009900>// Print out the strings:</font>
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> u = 0; stringStash[u]; u++)
    cout &lt;&lt; <font color=#004488>"stringStash["</font> &lt;&lt; u &lt;&lt; <font color=#004488>"] = "</font>
         &lt;&lt; *(string*)stringStash[u] &lt;&lt; endl;
  <font color=#009900>// Clean up:</font>
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> v = 0; v &lt; stringStash.count(); v++)
    <font color=#0000ff>delete</font> (string*)stringStash.remove(v);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As before, <B>Stash</B>es are created and
filled with information, but this time the information is the pointers resulting
from <B>new</B>-expressions. In the first case, note the line:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>intStash.add(<font color=#0000ff>new</font> <font color=#0000ff>int</font>(i));</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The expression <B>new int(i)</B> uses the
pseudo-constructor form<A NAME="Index2204"></A><A NAME="Index2205"></A>, so
storage for a new <B>int</B> object is created on the heap, and the <B>int</B>
is initialized to the value <B>i</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">During printing, the value returned by
<B>PStash::operator[ ]</B> must be cast to the proper type; this is repeated for
the rest of the <B>PStash</B> objects in the program. It&#8217;s an undesirable
effect of using <B>void</B> pointers
<A NAME="Index2206"></A><A NAME="Index2207"></A>as the underlying representation
and will be fixed in later chapters.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The second test opens the source code
file and reads it one line at a time into another <B>PStash</B>. Each line is
read into a <A NAME="Index2208"></A><B>string</B> using
<A NAME="Index2209"></A><B>getline(&#160;)</B>, then a <B>new</B> <B>string</B>
is created from <B>line</B> to make an independent copy of that line. If we just
passed in the address of <B>line </B>each time, we&#8217;d get a whole bunch of
pointers pointing to <B>line</B>, which would only contain the last line that
was read from the file.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When fetching the pointers, you see the
expression:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>*(string*)stringStash[v]</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The pointer returned from <B>operator[
]</B> must be cast to a <B>string*</B> to give it the proper type. Then the
<B>string*</B> is dereferenced so the expression evaluates to an object, at
which point the compiler sees a <B>string</B> object to send to
<B>cout</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The objects created on the heap must be
destroyed through the use of the <B>remove(&#160;) </B>statement or else
you&#8217;ll get a message at runtime telling you that you haven&#8217;t
completely cleaned up the objects in the <B>PStash</B>.<B> </B>Notice that in
the case of the <B>int</B> pointers, no cast is necessary because there&#8217;s
no destructor for an <B>int</B> and all we need is memory
release:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> intStash.remove(k);</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, for the <B>string</B> pointers,
if you forget to do the cast you&#8217;ll have another (quiet) memory leak, so
the cast is essential:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> (string*)stringStash.remove(k);</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Some of these issues (but not all) can be
removed using templates (which you&#8217;ll learn about in Chapter
16).</FONT><A NAME="_Toc305593245"></A><A NAME="_Toc305628717"></A><A NAME="_Toc312374002"></A><A NAME="_Toc472654977"></A><BR></P></DIV>
<A NAME="Heading393"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
new &amp; delete for
arrays<BR><A NAME="Index2210"></A><A NAME="Index2211"></A><A NAME="Index2212"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C++, you can create arrays of objects
on the stack or on the heap with equal ease, and (of course) the constructor is
called for each object in the array. There&#8217;s one constraint, however:
There must be a default
constructor<A NAME="Index2213"></A><A NAME="Index2214"></A>, except for
aggregate initialization on the stack (see Chapter 6), because a constructor
with no arguments must be called for every object.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When creating arrays of objects on the
heap using <B>new</B>, there&#8217;s something else you must do. An example of
such an array is</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType* fp = <font color=#0000ff>new</font> MyType[100];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This allocates enough storage on the heap
for 100 <B>MyType</B> objects and calls the constructor for each one. Now,
however, you simply have a <B>MyType*</B>, which is exactly the same as
you&#8217;d get if you said</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType* fp2 = <font color=#0000ff>new</font> MyType;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">to create a single object. Because you
wrote the code, you know that <B>fp</B> is actually the starting address of an
array, so it makes sense to select array elements using an expression like
<B>fp[3]</B>. But what happens when you destroy the array? The
statements</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> fp2; <font color=#009900>// OK</font>
<font color=#0000ff>delete</font> fp;  <font color=#009900>// Not the desired effect</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">look exactly the same, and their effect
will be the same. The destructor will be called for the <B>MyType</B> object
pointed to by the given address, and then the storage will be released. For
<B>fp2</B> this is fine, but for <B>fp</B> this means that the other 99
destructor calls won&#8217;t be made. The proper amount of storage will still be
released, however, because it is allocated in one big chunk, and the size of the
whole chunk is stashed somewhere by the allocation routine.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution requires you to give the
compiler the information that this is actually the starting address of an array.
This is accomplished with the following syntax:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> []fp;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The empty brackets tell the compiler to
generate code that fetches the number of objects in the array, stored somewhere
when the array is created, and calls the destructor for that many array objects.
This is actually an improved syntax from the earlier form, which you may still
occasionally see in old code:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> [100]fp;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">which forced the programmer to include
the number of objects in the array and introduced the possibility that the
programmer would get it wrong. The additional overhead of letting the compiler
handle it was very low, and it was considered better to specify the number of
objects in one place instead of
two.</FONT><A NAME="_Toc312374003"></A><A NAME="_Toc472654978"></A><BR></P></DIV>
<A NAME="Heading394"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Making a pointer more like an
array<BR><A NAME="Index2215"></A><A NAME="Index2216"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As an aside, the <B>fp</B> defined above
can be changed to point to anything, which doesn&#8217;t make sense for the
starting address of an array. It makes more sense to define it as a constant, so
any attempt to modify the pointer will be flagged as an error. To get this
effect, you might try</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> <font color=#0000ff>const</font>* q = <font color=#0000ff>new</font> <font color=#0000ff>int</font>[10];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">or</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>const</font> <font color=#0000ff>int</font>* q = <font color=#0000ff>new</font> <font color=#0000ff>int</font>[10];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">but in both cases the <B>const</B> will
bind to the <B>int</B>, that is, what is being pointed <I>to</I>, rather than
the quality of the pointer itself. Instead, you must say</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font>* <font color=#0000ff>const</font> q = <font color=#0000ff>new</font> <font color=#0000ff>int</font>[10];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now the array elements in <B>q</B> can be
modified, but any change to <B>q</B> (like <B>q++</B>) is illegal, as it is with
an ordinary array
identifier.</FONT><A NAME="_Toc305593246"></A><A NAME="_Toc305628718"></A><A NAME="_Toc312374004"></A><A NAME="_Toc472654979"></A><BR></P></DIV>
<A NAME="Heading395"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Running out of storage<BR><A NAME="Index2217"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">What happens when the <B>operator new<U>(
)<A NAME="Index2218"></A><A NAME="Index2219"></A></U></B> cannot find a
contiguous block of storage large enough to hold the desired object? A special
function called the <I>new-handler<A NAME="Index2220"></A></I> is called. Or
rather, a pointer to a function is checked, and if the pointer is nonzero, then
the function it points to is called. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The default behavior for the new-handler
is to <A NAME="Index2221"></A><I>throw an exception</I>, a subject covered in
Volume 2. However, if you&#8217;re using heap allocation in your program,
it&#8217;s wise to at least replace the new-handler with a message that says
you&#8217;ve run out of memory and then aborts the program. That way, during
debugging, you&#8217;ll have a clue about what happened. For the final program
you&#8217;ll want to use more robust recovery.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You replace the new-handler by including
<B>new.h </B>and then calling <B>set_new_handler(&#160;)</B> with the address of
the function you want installed:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:NewHandler.cpp</font>
<font color=#009900>// Changing the new-handler</font>
#include &lt;iostream&gt;
#include &lt;cstdlib&gt;
#include &lt;<font color=#0000ff>new</font>&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> count = 0;

<font color=#0000ff>void</font> out_of_memory() {
  cerr &lt;&lt; <font color=#004488>"memory exhausted after "</font> &lt;&lt; count 
    &lt;&lt; <font color=#004488>" allocations!"</font> &lt;&lt; endl;
  exit(1);
}

<font color=#0000ff>int</font> main() {
  set_new_handler(out_of_memory);
  <font color=#0000ff>while</font>(1) {
    count++;
    <font color=#0000ff>new</font> <font color=#0000ff>int</font>[1000]; <font color=#009900>// Exhausts memory</font>
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The new-handler function must take no
arguments and have a <B>void</B> return value. The <B>while</B> loop will keep
allocating <B>int</B> objects (and throwing away their return addresses) until
the free store is exhausted. At the very next call to <B>new</B>, no storage can
be allocated, so the new-handler will be called.</FONT><BR></P></DIV>

⌨️ 快捷键说明

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