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

📄 chapter13.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:

<font color=#0000ff>int</font> main() {
  Object* a = <font color=#0000ff>new</font> Object(40, 'a');
  <font color=#0000ff>delete</font> a;
  <font color=#0000ff>void</font>* b = <font color=#0000ff>new</font> Object(40, 'b');
  <font color=#0000ff>delete</font> b;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>Object</B> contains a
<B>void*</B> that is initialized to &#8220;raw&#8221; data (it doesn&#8217;t
point to objects that have destructors). In the <B>Object</B> destructor,
<B>delete</B> is called for this <B>void*</B> with no ill effects, since the
only thing we need to happen is for the storage to be released.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, in <B>main(&#160;)</B> you can
see that it&#8217;s very necessary that <B>delete</B> know what type of object
it&#8217;s working with. Here&#8217;s the output:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Constructing object a, size = 40
Destructing object a
Constructing object b, size = 40</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because <B>delete a</B> knows that
<B>a</B> points to an <B>Object</B>, the destructor is called and thus the
storage allocated for <B>data</B> is released. However, if you manipulate an
object through a <B>void*</B> as in the case of <B>delete b</B>, the only thing
that happens is that the storage for the <B>Object</B> is released &#8211; but
the destructor is not called so there is no release of the memory that
<B>data</B> points to. When this program compiles, you probably won&#8217;t see
any warning messages; the compiler assumes you know what you&#8217;re doing. So
you get a very quiet memory leak.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you have a
<A NAME="Index2196"></A>memory leak in your program, search through all the
<B>delete</B> statements and check the type of pointer being deleted. If
it&#8217;s a <B>void*</B> then you&#8217;ve probably found one source of your
memory leak (C++ provides ample other opportunities for memory leaks,
however).</FONT><A NAME="_Toc472654975"></A><BR></P></DIV>
<A NAME="Heading390"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Cleanup responsibility with pointers</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To make the <B>Stash </B>and <B>Stack
</B>containers flexible (able to hold any type of object), they will hold
<B>void </B>pointers. This means that when a pointer is returned from the
<B>Stash</B> or <B>Stack</B> object, you must cast it to the proper type before
using it; as seen above, you must also cast it to the proper type before
deleting it or you&#8217;ll get a memory leak.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The other memory leak issue has to do
with making sure that <B>delete </B>is actually called for each object pointer
held in the container. The container cannot &#8220;own&#8221; the pointer
because it holds it as a <B>void*</B> and thus cannot perform the proper
cleanup. The user must be responsible for cleaning up the objects. This produces
a serious problem if you add pointers to objects created on the stack <I>and
</I>objects created on the heap to the same container because a
delete-expression is unsafe for a pointer that hasn&#8217;t been allocated on
the heap. (And when you fetch a pointer back from the container, how will you
know where its object has been allocated?) Thus, you must be sure that objects
stored in the following versions of <B>Stash </B>and <B>Stack </B>are made only
on the heap, either through careful programming or by creating classes that can
only be built on the heap.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s also important to make sure
that the client programmer takes responsibility for cleaning up all the pointers
in the container. You&#8217;ve seen in previous examples how the <B>Stack</B>
class checks in its destructor that all the <B>Link</B> objects have been
popped. For a <B>Stash</B> of pointers, however, another approach is
needed.</FONT><A NAME="_Toc312374000"></A><A NAME="_Toc472654976"></A><BR></P></DIV>
<A NAME="Heading391"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Stash for pointers<BR><A NAME="Index2197"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This new version of the <B>Stash</B>
class, called <B>PStash</B>, holds <I>pointers</I> to objects that exist by
themselves on the heap, whereas the old <B>Stash</B> in earlier chapters copied
the objects by value into the <B>Stash</B> container. Using <B>new</B> and
<B>delete</B>, it&#8217;s easy and safe to hold pointers to objects that have
been created on the heap.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s the header file for the
&#8220;pointer <B>Stash</B>&#8221;:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:PStash.h</font>
<font color=#009900>// Holds pointers instead of objects</font>
#ifndef PSTASH_H
#define PSTASH_H

<font color=#0000ff>class</font> PStash {
  <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>// Pointer storage:</font>
  <font color=#0000ff>void</font>** storage;
  <font color=#0000ff>void</font> inflate(<font color=#0000ff>int</font> increase);
<font color=#0000ff>public</font>:
  PStash() : quantity(0), storage(0), next(0) {}
  ~PStash();
  <font color=#0000ff>int</font> add(<font color=#0000ff>void</font>* element);
  <font color=#0000ff>void</font>* <font color=#0000ff>operator</font>[](<font color=#0000ff>int</font> index) <font color=#0000ff>const</font>; <font color=#009900>// Fetch</font>
  <font color=#009900>// Remove the reference from this PStash:</font>
  <font color=#0000ff>void</font>* remove(<font color=#0000ff>int</font> index);
  <font color=#009900>// Number of elements in Stash:</font>
  <font color=#0000ff>int</font> count() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> next; }
};
#endif <font color=#009900>// PSTASH_H ///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The underlying data elements are fairly
similar, but now <B>storage</B> is an array of <B>void </B>pointers, and the
allocation of storage for that array is performed with
<A NAME="Index2198"></A><B>new</B> instead of <B>malloc(&#160;)</B>. In the
expression</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font>** st = <font color=#0000ff>new</font> <font color=#0000ff>void</font>*[quantity + increase];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">the type of object allocated is a
<B>void*</B>, so the expression allocates an array of <B>void</B>
pointers.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The destructor deletes the storage where
the <B>void</B> pointers are held rather than attempting to delete what they
point at (which, as previously noted, will release their storage and not call
the destructors because a <B>void</B>
pointer<A NAME="Index2199"></A><A NAME="Index2200"></A> has no type
information).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The other change is the replacement of
the <B>fetch(&#160;)</B> function with <B>operator[
]<A NAME="Index2201"></A></B>, which makes more sense syntactically. Again,
however, a <B>void*</B> is returned, so the user must remember what types are
stored in the container and cast the pointers when fetching them out (a problem
that will be repaired in future chapters).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here are the member function
definitions:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:PStash.cpp {O}</font>
<font color=#009900>// Pointer Stash definitions</font>
#include <font color=#004488>"PStash.h"</font>
#include <font color=#004488>"../require.h"</font>
#include &lt;iostream&gt;
#include &lt;cstring&gt; <font color=#009900>// 'mem' functions</font>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> PStash::add(<font color=#0000ff>void</font>* element) {
  <font color=#0000ff>const</font> <font color=#0000ff>int</font> inflateSize = 10;
  <font color=#0000ff>if</font>(next &gt;= quantity)
    inflate(inflateSize);
  storage[next++] = element;
  <font color=#0000ff>return</font>(next - 1); <font color=#009900>// Index number</font>
}

<font color=#009900>// No ownership:</font>
PStash::~PStash() {
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; next; i++)
    require(storage[i] == 0, 
      <font color=#004488>"PStash not cleaned up"</font>);
  <font color=#0000ff>delete</font> []storage; 
}

<font color=#009900>// Operator overloading replacement for fetch</font>
<font color=#0000ff>void</font>* PStash::<font color=#0000ff>operator</font>[](<font color=#0000ff>int</font> index) <font color=#0000ff>const</font> {
  require(index &gt;= 0,
    <font color=#004488>"PStash::operator[] index negative"</font>);
  <font color=#0000ff>if</font>(index &gt;= 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];
}

<font color=#0000ff>void</font>* PStash::remove(<font color=#0000ff>int</font> index) {
  <font color=#0000ff>void</font>* v = <font color=#0000ff>operator</font>[](index);
  <font color=#009900>// "Remove" the pointer:</font>
  <font color=#0000ff>if</font>(v != 0) storage[index] = 0;
  <font color=#0000ff>return</font> v;
}

<font color=#0000ff>void</font> PStash::inflate(<font color=#0000ff>int</font> increase) {
  <font color=#0000ff>const</font> <font color=#0000ff>int</font> psz = <font color=#0000ff>sizeof</font>(<font color=#0000ff>void</font>*);
  <font color=#0000ff>void</font>** st = <font color=#0000ff>new</font> <font color=#0000ff>void</font>*[quantity + increase];
  memset(st, 0, (quantity + increase) * psz);
  memcpy(st, storage, quantity * psz);
  quantity += increase;
  <font color=#0000ff>delete</font> []storage; <font color=#009900>// Old storage</font>
  storage = st; <font color=#009900>// Point to new memory</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>add(&#160;)</B> function is
effectively the same as before, except that a pointer is stored instead of a
copy of the whole object.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>inflate(&#160;)</B> code is
modified to handle the allocation of an array of <B>void*</B> instead of the
previous design, which was only working with raw bytes. Here, instead of using
the prior approach of copying by array indexing, the Standard C library function
<A NAME="Index2202"></A><B>memset(&#160;)</B> is first used to set all the new
memory to zero (this is not strictly necessary, since the <B>PStash</B> is
presumably managing all the memory correctly &#8211; but it usually
doesn&#8217;t hurt to throw in a bit of extra care). Then
<A NAME="Index2203"></A><B>memcpy(&#160;)</B> moves the existing data from the
old location to the new. Often, functions like <B>memset(&#160;)</B> and
<B>memcpy(&#160;)</B> have been optimized over time, so they may be faster than
the loops shown previously. But with a function like <B>inflate(&#160;) </B>that
will probably not be used that often you may not see a performance difference.
However, the fact that the function calls are more concise than the loops may
help prevent coding errors.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To put the responsibility of object
cleanup squarely on the shoulders of the client programmer, there are two ways
to access the pointers in the <B>PStash</B>: the <B>operator[]</B>, which simply
returns the pointer but leaves it as a member of the container, and a second
member function <B>remove(&#160;)</B>, which returns the pointer but also
removes it from the container by assigning that position to zero. When the
destructor for <B>PStash </B>is called, it checks to make sure that all object
pointers have been removed; if not, you&#8217;re notified so you can prevent a
memory leak (more elegant solutions will be forthcoming in later
chapters).</FONT><BR></P></DIV>
<A NAME="Heading392"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
A test</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s the old test program for
<B>Stash</B> rewritten for the <B>PStash</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:PStashTest.cpp</font>
<font color=#009900>//{L} PStash</font>

⌨️ 快捷键说明

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