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

📄 chapter13.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The behavior of the new-handler is tied
to <B>operator new<U>( )</U></B>, so if you overload <B>operator new<U>(
)</U></B> (covered in the next section) the new-handler will not be called by
default. If you still want the new-handler to be called you&#8217;ll have to
write the code to do so inside your overloaded <B>operator new<U>(
)</U></B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, you can write more
sophisticated new-handlers, even one to try to reclaim memory (commonly known as
a <I>garbage collector<A NAME="Index2222"></A></I>). This is not a job for the
novice
programmer.</FONT><A NAME="_Toc305593247"></A><A NAME="_Toc305628719"></A><A NAME="_Toc312374005"></A><A NAME="_Toc472654980"></A><BR></P></DIV>
<A NAME="Heading396"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Overloading new &amp;
delete<BR><A NAME="Index2223"></A><A NAME="Index2224"></A><A NAME="Index2225"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you create a
new-expression<A NAME="Index2226"></A>, two things occur. First, storage is
allocated using the <B>operator new<U>( )</U></B>, then the constructor is
called. In a delete-expression<A NAME="Index2227"></A>, the destructor is
called, then storage is deallocated using the <B>operator delete<U>( )</U></B>.
The constructor and destructor calls are never under your control (otherwise you
might accidentally subvert them), but you <I>can </I>change the storage
allocation functions <B>operator new<U>( )</U></B> and <B>operator delete<U>(
)</U></B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The memory allocation
<A NAME="Index2228"></A><A NAME="Index2229"></A>system used by <B>new</B> and
<B>delete</B> is designed for general-purpose use. In special situations,
however, it doesn&#8217;t serve your needs. The most common reason to change the
allocator is <A NAME="Index2230"></A>efficiency: You might be creating and
destroying so many objects of a particular class that it has become a speed
bottleneck. C++ allows you to overload <B>new</B> and <B>delete</B> to implement
your own storage allocation scheme, so you can handle problems like
this.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Another issue is
<A NAME="Index2231"></A><A NAME="Index2232"></A>heap fragmentation. By
allocating objects of different sizes it&#8217;s possible to break up the heap
so that you effectively run out of storage. That is, the storage might be
available, but because of fragmentation no piece is big enough to satisfy your
needs. By creating your own allocator for a particular class, you can ensure
this never happens.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In embedded and real-time systems, a
program may have to run for a very long time with restricted resources. Such a
system may also require that memory allocation always take the same amount of
time, and there&#8217;s no allowance for heap exhaustion or fragmentation. A
custom memory allocator is the solution; otherwise, programmers will avoid using
<B>new</B> and <B>delete</B> altogether in such cases and miss out on a valuable
C++ asset.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you overload <B>operator new<U>(
)</U></B> and <B>operator delete<U>( )</U></B>, it&#8217;s important to remember
that you&#8217;re changing only the way <I>raw storage is allocated</I>. The
compiler will simply call your <B>new</B> instead of the default version to
allocate storage, then call the constructor for that storage. So, although the
compiler allocates storage <I>and</I> calls the constructor when it sees
<B>new</B>, all you can change when you overload <B>new</B> is the storage
allocation portion. (<B>delete</B> has a similar limitation.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you overload <B>operator</B>
<B>new<U>( )</U></B>, you also replace the behavior when it runs out of memory,
so you must decide what to do in your <B>operator new<U>( )</U></B>: return
zero, write a loop to call the new-handler and retry allocation, or (typically)
throw a <B>bad_alloc</B> exception (discussed in Volume 2, available at
<I>www.BruceEckel.com</I>).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Overloading <B>new</B> and <B>delete</B>
is like overloading any other operator. However, you have a choice of
overloading the global allocator or using a different allocator for a particular
class.</FONT><A NAME="_Toc312374006"></A><A NAME="_Toc472654981"></A><BR></P></DIV>
<A NAME="Heading397"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Overloading global new &amp;
delete<BR><A NAME="Index2233"></A><A NAME="Index2234"></A><A NAME="Index2235"></A><A NAME="Index2236"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is the drastic approach, when the
global versions of <B>new</B> and <B>delete</B> are unsatisfactory for the whole
system. If you overload the global versions, you make the defaults completely
inaccessible &#8211; you can&#8217;t even call them from inside your
redefinitions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The overloaded <B>new</B> must take an
argument of <B>size_t<A NAME="Index2237"></A></B> (the Standard C standard type
for sizes). This argument is generated and passed to you by the compiler and is
the size of the object you&#8217;re responsible for allocating. You must return
a pointer either to an object of that size (or bigger, if you have some reason
to do so), or to zero if you can&#8217;t find the memory (in which case the
constructor is <I>not</I> called!). However, if you can&#8217;t find the memory,
you should probably do something more informative than just returning zero, like
calling the new-handler or throwing an exception, to signal that there&#8217;s a
problem.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The return value of <B>operator new<U>(
)</U></B> is a <B>void*</B>, <I>not</I> a pointer to any particular type. All
you&#8217;ve done is produce memory, not a finished object &#8211; that
doesn&#8217;t happen until the constructor is called, an act the compiler
guarantees and which is out of your control.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>operator delete<U>( )</U></B>
takes a <B>void*</B> to memory that was allocated by <B>operator new</B>.
It&#8217;s a <B>void*</B> because <B>operator delete </B>only gets the pointer
<I>after</I> the destructor is called, which removes the object-ness from the
piece of storage. The return type is <B>void</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s a simple example showing how
to overload the global <B>new</B> and <B>delete</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:GlobalOperatorNew.cpp</font>
<font color=#009900>// Overload global new/delete</font>
#include &lt;cstdio&gt;
#include &lt;cstdlib&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>void</font>* <font color=#0000ff>operator</font> <font color=#0000ff>new</font>(size_t sz) {
  printf(<font color=#004488>"operator new: %d Bytes\n"</font>, sz);
  <font color=#0000ff>void</font>* m = malloc(sz);
  <font color=#0000ff>if</font>(!m) puts(<font color=#004488>"out of memory"</font>);
  <font color=#0000ff>return</font> m;
}

<font color=#0000ff>void</font> <font color=#0000ff>operator</font> <font color=#0000ff>delete</font>(<font color=#0000ff>void</font>* m) {
  puts(<font color=#004488>"operator delete"</font>);
  free(m);
}

<font color=#0000ff>class</font> S {
  <font color=#0000ff>int</font> i[100];
<font color=#0000ff>public</font>:
  S() { puts(<font color=#004488>"S::S()"</font>); }
  ~S() { puts(<font color=#004488>"S::~S()"</font>); }
};

<font color=#0000ff>int</font> main() {
  puts(<font color=#004488>"creating &amp; destroying an int"</font>);
  <font color=#0000ff>int</font>* p = <font color=#0000ff>new</font> <font color=#0000ff>int</font>(47);
  <font color=#0000ff>delete</font> p;
  puts(<font color=#004488>"creating &amp; destroying an s"</font>);
  S* s = <font color=#0000ff>new</font> S;
  <font color=#0000ff>delete</font> s;
  puts(<font color=#004488>"creating &amp; destroying S[3]"</font>);
  S* sa = <font color=#0000ff>new</font> S[3];
  <font color=#0000ff>delete</font> []sa;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here you can see the general form for
overloading <B>new</B> and <B>delete</B>. These use the Standard C library
functions <A NAME="Index2238"></A><B>malloc(&#160;)</B> and
<A NAME="Index2239"></A><B>free(&#160;)</B> for the allocators (which is
probably what the default <B>new</B> and <B>delete</B> use as well!). However,
they also print messages about what they are doing. Notice that
<A NAME="Index2240"></A><B>printf(&#160;)</B> and
<A NAME="Index2241"></A><B>puts(&#160;)</B> are used rather than
<B>iostreams</B>. This is because when an <B>iostream</B>
<A NAME="Index2242"></A>object is created (like the global <B>cin</B>,
<B>cout</B>, and <B>cerr</B>), it calls <B>new</B> to allocate memory. With
<B>printf(&#160;)</B>, you don&#8217;t get into a deadlock because it
doesn&#8217;t call <B>new </B>to initialize itself.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, objects of
built-in types are created to prove that the overloaded <B>new</B> and
<B>delete</B> are also called in that case. Then a single object of type
<B>S</B> is created, followed by an array of <B>S</B>. For the array,
you&#8217;ll see from the number of bytes requested that extra memory is
allocated to store information (inside the array) about the number of objects it
holds. In all cases, the global overloaded versions of <B>new</B> and
<B>delete</B> are
used.</FONT><A NAME="_Toc312374007"></A><A NAME="_Toc472654982"></A><BR></P></DIV>
<A NAME="Heading398"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Overloading new &amp; delete for a
class<BR><A NAME="Index2243"></A><A NAME="Index2244"></A><A NAME="Index2245"></A><A NAME="Index2246"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although you don&#8217;t have to
explicitly say <B>static</B>, when you overload <B>new </B>and <B>delete</B> for
a class, you&#8217;re creating <B>static</B> member functions. As before, the
syntax is the same as overloading any other operator. When the compiler sees you
use <B>new</B> to create an object of your class, it chooses the member
<B>operator new<U>( )</U></B> over the global version. However, the global
versions of <B>new</B> and <B>delete</B> are used for all other types of objects
(unless they have their own <B>new</B> and <B>delete</B>).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the following example, a primitive
storage allocation system
<A NAME="Index2247"></A><A NAME="Index2248"></A><A NAME="Index2249"></A>is
created for the class <B>Framis</B>. A chunk of memory is set aside in the
static data area at program start-up, and that memory is used to allocate space
for objects of type <B>Framis</B>. To determine which blocks have been
allocated, a simple array of bytes is used, one byte for each
block:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C13:Framis.cpp</font>
<font color=#009900>// Local overloaded new &amp; delete</font>
#include &lt;cstddef&gt; <font color=#009900>// Size_t</font>
#include &lt;fstream&gt;
#include &lt;iostream&gt;
#include &lt;<font color=#0000ff>new</font>&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
ofstream out(<font color=#004488>"Framis.out"</font>);

<font color=#0000ff>class</font> Framis {
  <font color=#0000ff>enum</font> { sz = 10 };
  <font color=#0000ff>char</font> c[sz]; <font color=#009900>// To take up space, not used</font>
  <font color=#0000ff>static</font> <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font> pool[];
  <font color=#0000ff>static</font> <font color=#0000ff>bool</font> alloc_map[];
<font color=#0000ff>public</font>:
  <font color=#0000ff>enum</font> { psize = 100 };  <font color=#009900>// frami allowed</font>
  Framis() { out &lt;&lt; <font color=#004488>"Framis()\n"</font>; }
  ~Framis() { out &lt;&lt; <font color=#004488>"~Framis() ... "</font>; }
  <font color=#0000ff>void</font>* <font color=#0000ff>operator</font> <font color=#0000ff>new</font>(size_t) <font color=#0000ff>throw</font>(bad_alloc);
  <font color=#0000ff>void</font> <font color=#0000ff>operator</font> <font color=#0000ff>delete</font>(<font color=#0000ff>void</font>*);
};
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font> Framis::pool[psize * <font color=#0000ff>sizeof</font>(Framis)];
<font color=#0000ff>bool</font> Framis::alloc_map[psize] = {<font color=#0000ff>false</font>};

<font color=#009900>// Size is ignored -- assume a Framis object</font>
<font color=#0000ff>void</font>* 
Framis::<font color=#0000ff>operator</font> <font color=#0000ff>new</font>(size_t) <font color=#0000ff>throw</font>(bad_alloc) {
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; psize; i++)
    <font color=#0000ff>if</font>(!alloc_map[i]) {
      out &lt;&lt; <font color=#004488>"using block "</font> &lt;&lt; i &lt;&lt; <font color=#004488>" ... "</font

⌨️ 快捷键说明

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