📄 chapter13.html
字号:
<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 “raw” data (it doesn’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( )</B> you can
see that it’s very necessary that <B>delete</B> know what type of object
it’s working with. Here’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 – 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’t see
any warning messages; the compiler assumes you know what you’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’s a <B>void*</B> then you’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’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 “own” 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’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’s also important to make sure
that the client programmer takes responsibility for cleaning up all the pointers
in the container. You’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’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’s the header file for the
“pointer <B>Stash</B>”:</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( )</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( )</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 <iostream>
#include <cstring> <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 >= 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 < 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 >= 0,
<font color=#004488>"PStash::operator[] index negative"</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];
}
<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( )</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( )</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( )</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 – but it usually
doesn’t hurt to throw in a bit of extra care). Then
<A NAME="Index2203"></A><B>memcpy( )</B> moves the existing data from the
old location to the new. Often, functions like <B>memset( )</B> and
<B>memcpy( )</B> have been optimized over time, so they may be faster than
the loops shown previously. But with a function like <B>inflate( ) </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( )</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’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’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 + -