📄 chapter13.html
字号:
line:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Obj->initialize();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If users make it this far correctly, they
must remember to initialize the object before it is used. Notice that a
constructor was not used because the constructor <A NAME="Index2159"></A>cannot
be called
explicitly</FONT><A NAME="fnB50" HREF="#fn50">[50]</A><FONT FACE="Georgia">
– it’s called for you by the compiler when an object is created. The
problem here is that the user now has the option to forget to perform the
initialization before the object is used, thus reintroducing a major source of
bugs.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It also turns out that many programmers
seem to find C’s dynamic memory functions too confusing and complicated;
it’s not uncommon to find C programmers who use virtual memory
<A NAME="Index2160"></A>machines allocating huge arrays of variables in the
static storage area to avoid thinking about dynamic memory allocation. Because
C++ is attempting to make library use safe and effortless for the casual
programmer, C’s approach to dynamic memory is
unacceptable.</FONT><A NAME="_Toc312373994"></A><A NAME="_Toc472654969"></A><BR></P></DIV>
<A NAME="Heading384"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
operator new</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution in C++ is to combine all the
actions necessary to create an object into a single operator called
<B>new<A NAME="Index2161"></A><A NAME="Index2162"></A></B>. When you create an
object with <B>new</B> (using a
<I>new-expression<A NAME="Index2163"></A><A NAME="Index2164"></A></I>), it
allocates enough storage on the heap to hold the object and calls the
constructor for that storage. Thus, if you say</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType *fp = <font color=#0000ff>new</font> MyType(1,2);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">at runtime, the equivalent of
<B>malloc(sizeof(MyType))</B> is called (often, it is literally a call to
<B>malloc( )<A NAME="Index2165"></A></B>), and the constructor for
<B>MyType</B> is called with the resulting address as the <B>this</B>
<A NAME="Index2166"></A>pointer, using <B>(1,2)</B> as the argument list. By the
time the pointer is assigned to <B>fp</B>, it’s a live, initialized object
– you can’t even get your hands on it before then. It’s also
automatically the proper <B>MyType</B> type so no cast
<A NAME="Index2167"></A>is necessary.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The default <B>new</B> checks to make
sure the memory allocation was successful before passing the address to the
constructor, so you don’t have to explicitly determine if the call was
successful. Later in the chapter you’ll find out what happens if
there’s no memory left.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can create a new-expression using any
constructor available for the class. If the constructor has no arguments, you
write the new-expression without the constructor argument list:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType *fp = <font color=#0000ff>new</font> MyType;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice how simple the process of creating
objects on the heap becomes – a single expression, with all the sizing,
conversions, and safety checks built in. It’s as easy to create an object
on the heap as it is on the
stack.</FONT><A NAME="_Toc312373995"></A><A NAME="_Toc472654970"></A><BR></P></DIV>
<A NAME="Heading385"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
operator delete</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The complement to the new-expression is
the <I>delete-expression<A NAME="Index2168"></A></I>, which first calls the
destructor and then releases the memory (often with a call to
<B>free( )<A NAME="Index2169"></A></B>). Just as a new-expression returns a
pointer to the object, a delete-expression requires the address of an
object.</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">This destructs and then releases the
storage for the dynamically allocated <B>MyType</B> object created
earlier.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>delete<A NAME="Index2170"></A></B> can
be called only for an object created by <B>new</B>. If you <B>malloc( )</B>
(or <B>calloc( )</B> or <B>realloc( )</B>) an object and then
<B>delete</B> it, the behavior is undefined. Because most default
implementations of <B>new</B> and <B>delete</B> use <B>malloc( )</B> and
<B>free( )</B>, you’d probably end up releasing the memory without
calling the destructor.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If the pointer you’re deleting is
zero<A NAME="Index2171"></A>, nothing will happen. For this reason, people often
recommend setting a pointer to zero immediately after you delete it, to prevent
deleting it twice. Deleting an object more than once is definitely a bad thing
to do, and will cause
problems<A NAME="Index2172"></A>.</FONT><A NAME="_Toc312373996"></A><A NAME="_Toc472654971"></A><BR></P></DIV>
<A NAME="Heading386"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
A simple example</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example shows that initialization
takes place:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:Tree.h</font>
#ifndef TREE_H
#define TREE_H
#include <iostream>
<font color=#0000ff>class</font> Tree {
<font color=#0000ff>int</font> height;
<font color=#0000ff>public</font>:
Tree(<font color=#0000ff>int</font> treeHeight) : height(treeHeight) {}
~Tree() { std::cout << <font color=#004488>"*"</font>; }
<font color=#0000ff>friend</font> std::ostream&
<font color=#0000ff>operator</font><<(std::ostream& os, <font color=#0000ff>const</font> Tree* t) {
<font color=#0000ff>return</font> os << <font color=#004488>"Tree height is: "</font>
<< t->height << std::endl;
}
};
#endif <font color=#009900>// TREE_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:NewAndDelete.cpp</font>
<font color=#009900>// Simple demo of new & delete</font>
#include <font color=#004488>"Tree.h"</font>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
Tree* t = <font color=#0000ff>new</font> Tree(40);
cout << t;
<font color=#0000ff>delete</font> t;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">We can prove that the constructor is
called by printing out the value of the <B>Tree</B>. Here, it’s done by
overloading the <B>operator<<</B> to use with an <B>ostream</B> and a
<B>Tree*<A NAME="Index2173"></A><A NAME="Index2174"></A><A NAME="Index2175"></A></B>.
Note, however, that even though the function is declared as a
<B>friend<A NAME="Index2176"></A></B>, it is defined as an inline! This is a
mere convenience – defining a <B>friend</B> function as an inline to a
class doesn’t change the <B>friend</B> status or the fact that it’s
a global function and not a class member function. Also notice that the return
value is the result of the entire output expression, which is an
<B>ostream&</B> (which it must be, to satisfy the return value type of the
function).</FONT><A NAME="_Toc312373997"></A><A NAME="_Toc472654972"></A><BR></P></DIV>
<A NAME="Heading387"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Memory manager overhead<BR><A NAME="Index2177"></A><A NAME="Index2178"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you create automatic objects on the
stack, the size of the objects
<A NAME="Index2179"></A><A NAME="Index2180"></A>and their lifetime is built
right into the generated code, because the compiler knows the exact type,
quantity, and scope<A NAME="Index2181"></A>. Creating objects on the heap
<A NAME="Index2182"></A><A NAME="Index2183"></A><A NAME="Index2184"></A>involves
additional overhead, both in time and in space. Here’s a typical scenario.
(You can replace <B>malloc( )</B> <A NAME="Index2185"></A>with
<B>calloc( )<A NAME="Index2186"></A></B> or
<B>realloc( )<A NAME="Index2187"></A></B>.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You call <B>malloc( )</B>, which
requests a block of memory from the pool. (This code may actually be part of
<B>malloc( )</B>.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The pool is searched for a block of
memory large enough to satisfy the request. This is done by checking a map or
directory of some sort that shows which blocks are currently in use and which
are available. It’s a quick process, but it may take several tries so it
might not be deterministic – that is, you can’t necessarily count on
<B>malloc( )<A NAME="Index2188"></A></B> always taking exactly the same
amount of time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Before a pointer to that block is
returned, the size and location of the block must be recorded so further calls
to <B>malloc( )</B> won’t use it, and so that when you call
<B>free( )<A NAME="Index2189"></A></B>, the system knows how much memory to
release.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The way all this is implemented can vary
widely. For example, there’s nothing to prevent primitives for memory
allocation being implemented in the processor. If you’re curious, you can
write test programs to try to guess the way your <B>malloc( )</B> is
implemented. You can also read the library source code, if you have it (the GNU
C sources are always
available).</FONT><A NAME="_Toc305593244"></A><A NAME="_Toc305628716"></A><A NAME="_Toc312373998"></A><A NAME="_Toc472654973"></A><BR></P></DIV>
<A NAME="Heading388"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Early examples redesigned</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Using <B>new</B> and <B>delete</B>, the
<B>Stash</B> example introduced previously in this book can be rewritten using
all the features discussed in the book so far. Examining the new code will also
give you a useful review of the topics.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At this point in the book, neither the
<B>Stash</B> nor <B>Stack</B> classes will
“own”<A NAME="Index2190"></A><A NAME="Index2191"></A> the objects
they point to; that is, when the <B>Stash</B> or <B>Stack</B> object goes out of
scope, it will not call <B>delete</B> for all the objects it points to. The
reason this is not possible is because, in an attempt to be generic, they hold
<B>void</B> <A NAME="Index2192"></A>pointers<A NAME="Index2193"></A>. If you
<B>delete</B> a <B>void</B> pointer, the only thing that happens is the memory
gets released, because there’s no type information and no way for the
compiler to know what destructor to
call.</FONT><A NAME="_Toc472654974"></A><BR></P></DIV>
<A NAME="Heading389"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
delete void* is probably a
bug<BR><A NAME="Index2194"></A><A NAME="Index2195"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s worth making a point that if
you call <B>delete</B> for a <B>void*</B>, it’s almost certainly going to
be a bug in your program unless the destination of that pointer is very simple;
in particular, it should not have a destructor. Here’s an example to show
you what happens:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:BadVoidPointerDeletion.cpp</font>
<font color=#009900>// Deleting void pointers can cause memory leaks</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Object {
<font color=#0000ff>void</font>* data; <font color=#009900>// Some storage</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> size;
<font color=#0000ff>const</font> <font color=#0000ff>char</font> id;
<font color=#0000ff>public</font>:
Object(<font color=#0000ff>int</font> sz, <font color=#0000ff>char</font> c) : size(sz), id(c) {
data = <font color=#0000ff>new</font> <font color=#0000ff>char</font>[size];
cout << <font color=#004488>"Constructing object "</font> << id
<< <font color=#004488>", size = "</font> << size << endl;
}
~Object() {
cout << <font color=#004488>"Destructing object "</font> << id << endl;
<font color=#0000ff>delete</font> []data; <font color=#009900>// OK, just releases storage,</font>
<font color=#009900>// no destructor calls are necessary</font>
}
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -