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

📄 ei7.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 7: Be prepared for out-of-memory conditions</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT>
var dingbase = "EI7_DIR.HTM";
var dingtext = "Item E7, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E7: Be prepared for out-of-memory conditions." -->
<A NAME="1894"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="EI6_FR.HTM" TARGET="_top">Item 6: Use delete on pointer members in destructors.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="EI8_FR.HTM" TARGET="_top">Item 8: Adhere to convention when writing operator new and operator delete.</A></FONT></DIV>

<P><A NAME="dingp1"></A><FONT ID="eititle">Item 7: &nbsp;Be prepared for out-of-memory conditions.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P><A NAME="1895"></A>
<P><A NAME="dingp2"></A>
When <CODE>operator</CODE> <CODE>new</CODE> can't allocate the memory you request, it throws an exception. (It used to return 0, and some older compilers still do that. You can make your compilers do it again if you want to, but I'll defer that discussion until the end of this Item.) Deep in your heart of hearts, you know that handling out-of-memory exceptions is the only truly moral course of action. At the same time, you are keenly aware of the fact that doing so is a pain in the neck. As a result, chances are that you omit such handling from time to time. Like always, perhaps. <A NAME="p26"></A>Still, you must harbor a lurking sense of guilt. I mean, what if <CODE>new</CODE> really <I>does</I> yield an <NOBR>exception?<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="223155"></A>
<P><A NAME="dingp3"></A>
You may think that one reasonable way to cope with this matter is to fall back on your days in the gutter, i.e., to use the preprocessor. For example, a common C idiom is to define a type-independent macro to allocate memory and then check to make sure the allocation succeeded. For C++, such a macro might look something like <NOBR>this:<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="223157"></A>
<UL><PRE>
#define NEW(PTR, TYPE)                       \
    try { (PTR) = new TYPE; }                \
    catch (std::bad_alloc&amp;) { assert(0); }
</PRE>
</UL><A NAME="223160"></A>
<P><A NAME="dingp4"></A>
("Wait! What's this <CODE>std::bad_alloc</CODE> business?", you ask. <CODE>bad_alloc</CODE> is the type of exception <CODE>operator</CODE> <CODE>new</CODE> throws when it can't satisfy a memory allocation request, and <CODE>std</CODE> is the name of the namespace (see <A HREF="./EI28_FR.HTM#6429" TARGET="_top">Item 28</A>) where <CODE>bad_alloc</CODE> is defined. "Okay," you continue, "what's this <CODE>assert</CODE> business?" Well, if you look in the standard C include file <CODE>&lt;assert.h&gt;</CODE> (or its namespace-savvy C++ equivalent, <CODE>&lt;cassert&gt;</CODE> &#151; see <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A>), you'll find that <CODE>assert</CODE> is a macro. The macro checks to see if the expression it's passed is non-zero, and, if it's not, it issues an error message and calls <CODE>abort</CODE>. Okay, it does that only when the standard macro <CODE>NDEBUG</CODE> isn't defined, i.e., in debug mode. In production mode, i.e., when <CODE>NDEBUG</CODE> is defined, <CODE>assert</CODE> expands to nothing &#151; to a <CODE>void</CODE> statement. You thus check <CODE>assert</CODE>ions only when <NOBR>debugging.)<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="223172"></A>
<P><A NAME="dingp5"></A>
This <CODE>NEW</CODE> macro suffers from the common error of using an <CODE>assert</CODE> to test a condition that might occur in production code (after all, you can run out of memory at any time), but it also has a drawback specific to C++: it fails to take into account the myriad ways in which <CODE>new</CODE> can be used. There are three common syntactic forms for getting new objects of type <CODE>T</CODE>, and you need to deal with the possibility of exceptions for each of these <NOBR>forms:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="1900"></A>
<UL><PRE>new T;
</PRE>
</UL><A NAME="1901"></A>
<UL><PRE>new T(<i>constructor arguments</i>);
</PRE>
</UL><A NAME="1902"></A>
<UL><PRE>new T[<i>size</i>];
</PRE>
</UL><A NAME="1903"></A>
<P><A NAME="dingp6"></A>
This oversimplifies the problem, however, because clients can define their own (overloaded) versions of <CODE>operator</CODE> <CODE>new</CODE>, so programs may contain an arbitrary number of different syntactic forms for using <CODE>new</CODE>.<SCRIPT>create_link(6);</SCRIPT>
</P>
<A NAME="1904"></A>
<P><A NAME="dingp7"></A>
How, then, to cope? If you're willing to settle for a very simple error-handling strategy, you can set things up so that if a request for memory cannot be satisfied, an error-handling function you specify is called. This strategy relies on the convention that when <CODE>operator</CODE> <CODE>new</CODE> cannot satisfy a request, it calls a client-specifiable error-handling function &#151; often called a <I>new-handler</I> &#151; before it throws an exception. (In truth, what <CODE>operator</CODE> <CODE>new</CODE> really does is slightly more complicated. Details are provided in <A HREF="./EI8_FR.HTM#120851" TARGET="_top">Item 8</A>.)<SCRIPT>create_link(7);</SCRIPT>
</P>
<A NAME="1905"></A>
<P><A NAME="dingp8"></A>
<A NAME="p27"></A>To specify the out-of-memory-handling function, clients call <CODE>set_new_handler</CODE>, which is specified in the header <CODE>&lt;new&gt;</CODE> more or less like <NOBR>this:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="13137"></A>
<UL><PRE>typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
</PRE>
</UL><A NAME="13231"></A>
<P><A NAME="dingp9"></A>
As you can see, <CODE>new_handler</CODE> is a typedef for a pointer to a function that takes and returns nothing, and <CODE>set_new_handler</CODE> is a function that takes and returns a <CODE>new_handler</CODE>.<SCRIPT>create_link(9);</SCRIPT>
</P>
<A NAME="13232"></A>
<P><A NAME="dingp10"></A>
<CODE>set_new_handler</CODE>'s parameter is a pointer to the function <CODE>operator</CODE> <CODE>new</CODE> should call if it can't allocate the requested memory. The return value of <CODE>set_new_handler</CODE> is a pointer to the function in effect for that purpose before <CODE>set_new_handler</CODE> was <NOBR>called.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="1909"></A>
<P><A NAME="dingp11"></A>
You use <CODE>set_new_handler</CODE> like <NOBR>this:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="1910"></A>
<UL><PRE>// function to call if operator new can't allocate enough memory
void noMoreMemory()
{
  cerr &lt;&lt; "Unable to satisfy request for memory\n";
  abort();
}
</PRE>
</UL><A NAME="1911"></A>
<UL><PRE>  int main()
{
  set_new_handler(noMoreMemory);
</PRE>
</UL><A NAME="1912"></A>
<UL><PRE>  int *pBigDataArray = new int[100000000];
</PRE>
</UL><A NAME="1913"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="1914"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="1915"></A>
<P><A NAME="dingp12"></A>
If, as seems likely, <CODE>operator</CODE> <CODE>new</CODE> is unable to allocate space for 100,000,000 integers, <CODE>noMoreMemory</CODE> will be called, and the program will abort after issuing an error message. This is a marginally better way to terminate the program than a simple core dump. (By the way, consider what happens if memory must be dynamically allocated during the course of writing the error message to <CODE>cerr</CODE>...)<SCRIPT>create_link(12);</SCRIPT>
</P>
<A NAME="29858"></A>
<P><A NAME="dingp13"></A>
When <CODE>operator</CODE> <CODE>new</CODE> cannot satisfy a request for memory, it calls the new-handler function not once, but <I>repeatedly</I> until it can find enough memory. The code giving rise to these repeated calls is shown in <A HREF="./EI8_FR.HTM#120851" TARGET="_top">Item 8</A>, but this high-level description is enough to conclude that a well-designed new-handler function must do one of the <NOBR>following:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P><UL><A NAME="29874"></A>
<A NAME="dingp14"></A><LI><B>Make more memory available.</B> This may allow <CODE>operator</CODE> <CODE>new</CODE>'s next attempt to allocate the memory to succeed. One way to implement this strategy is to allocate a large block of memory at pro<A NAME="p28"></A>gram start-up, then release it the first time the new-handler is invoked. Such a release is often accompanied by some kind of warning to the user that memory is low and that future requests may fail unless more memory is somehow made available.<SCRIPT>create_link(14);</SCRIPT>

<A NAME="29940"></A>
<A NAME="dingp15"></A><LI><B>Install a different new-handler.</B> If the current new-handler can't make any more memory available, perhaps it knows of a different new-handler that is more resourceful. If so, the current new-handler can install the other new-handler in its place (by calling <CODE>set_new_handler</CODE>). The next time <CODE>operator</CODE> <CODE>new</CODE> calls the new-handler function, it will get the one most recently installed. (A variation on this theme is for a new-handler to modify its <I>own</I> behavior, so the next time it's invoked, it does something different. One way to achieve this is to have the new-handler modify static or global data that affects the new-handler's behavior.)<SCRIPT>create_link(15);</SCRIPT>

<A NAME="29947"></A>
<A NAME="dingp16"></A><LI><B>Deinstall the new-handler,</B> i.e., pass the null pointer to <CODE>set_new_handler</CODE>. With no new-handler installed, <CODE>operator</CODE> <CODE>new</CODE> will throw an exception of type <CODE>std::bad_alloc</CODE> when its attempt to allocate memory is unsuccessful.<SCRIPT>create_link(16);</SCRIPT>

<A NAME="29964"></A>
<A NAME="dingp17"></A><LI><B>Throw an exception</B> of type <CODE>std::bad_alloc</CODE> or some type derived from <CODE>std::bad_alloc</CODE>. Such exceptions will not be caught by <CODE>operator</CODE> <CODE>new</CODE>, so they will propagate to the site originating the request for memory. (Throwing an exception of a different type will violate <CODE>operator</CODE> <CODE>new</CODE>'s exception specification. The default action when that happens is to call <CODE>abort</CODE>, so if your new-handler is going to throw an exception, you definitely want to make sure it's from the <CODE>std::bad_alloc</CODE> hierarchy. For more information on exception specifications, see <A HREF="../MEC/MI14_FR.HTM#6011" TARGET="_top">Item M14</A>.)<SCRIPT>create_link(17);</SCRIPT>

<A NAME="29975"></A>
<A NAME="dingp18"></A><LI><B>Not return</B>, typically by calling <CODE>abort</CODE> or <CODE>exit</CODE>, both of which are found in the standard C library (and thus in the standard C++ library &#151; see <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A>).<SCRIPT>create_link(18);</SCRIPT>

</UL>
<A NAME="29984"></A>
<P><A NAME="dingp19"></A>
These choices give you considerable flexibility in implementing new-handler <NOBR>functions.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="61541"></A>
<P><A NAME="dingp20"></A>
Sometimes you'd like to handle memory allocation failures in different ways, depending on the class of the object being <NOBR>allocated:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P><A NAME="61542"></A>
<UL><PRE>class X {
public:
  static void outOfMemory();
</PRE>
</UL><A NAME="1918"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="1919"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="1920"></A>
<UL><PRE><A NAME="p29"></A>class Y {
public:
  static void outOfMemory();
</PRE>
</UL><A NAME="1921"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="1922"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="1923"></A>
<UL><PRE>
X* p1 = new X;      // if allocation is unsuccessful,
                    // call X::outOfMemory
</PRE>
</UL><A NAME="1924"></A>
<UL><PRE>
Y* p2 = new Y;      // if allocation is unsuccessful,
                    // call Y::outOfMemory
</PRE>
</UL><A NAME="1925"></A>
<P><A NAME="dingp21"></A>
C++ has no support for class-specific new-handlers, but it doesn't need to. You can implement this behavior yourself. You just have each class provide its own versions of <CODE>set_new_handler</CODE> and <CODE>operator</CODE> <CODE>new</CODE>. The class's <CODE>set_new_handler</CODE> allows clients to specify the new-handler for the class (just like the standard <CODE>set_new_handler</CODE> allows clients to specify the global new-handler). The class's <CODE>operator</CODE> <CODE>new</CODE> ensures that the class-specific new-handler is used in place of the global new-handler when memory for class objects is <NOBR>allocated.<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="1928"></A>
<P><A NAME="dingp22"></A>
Consider a class <CODE>X</CODE> for which you want to handle memory allocation failures. You'll have to keep track of the function to call when <CODE>operator</CODE> <CODE>new</CODE> can't allocate enough memory for an object of type <CODE>X</CODE>, so you'll declare a static member of type <CODE>new_handler</CODE> to point to the new-handler function for the class. Your class <CODE>X</CODE> will look something like <NOBR>this:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="1929"></A>
<UL><PRE>class X {
public:
  static new_handler set_new_handler(new_handler p);
  static void * operator new(size_t size);
</PRE>
</UL><A NAME="13391"></A>
<UL><PRE>private:
  static new_handler currentHandler;
};
</PRE>
</UL><A NAME="1931"></A>
<P><A NAME="dingp23"></A>
Static class members must be defined outside the class definition. Because you'll want to use the default initialization of static objects to 0, you'll define <CODE>X::currentHandler</CODE> without initializing <NOBR>it:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="1932"></A>
<UL><PRE>
new_handler X::currentHandler;      // sets currentHandler
                                    // to 0 (i.e., null) by
                                    // default
</PRE>
</UL><A NAME="1933"></A>
<P><A NAME="dingp24"></A>
The <CODE>set_new_handler</CODE> function in class <CODE>X</CODE> will save whatever pointer is passed to it. It will return whatever pointer had been saved prior to the call. This is exactly what the standard version of <CODE>set_new_handler</CODE> <NOBR>does:<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="1934"></A>
<UL><PRE><A NAME="p30"></A>new_handler X::set_new_handler(new_handler p)
{
  new_handler oldHandler = currentHandler;
  currentHandler = p;
  return oldHandler;
}
</PRE>
</UL><A NAME="1935"></A>
<P><A NAME="dingp25"></A>
Finally, <CODE>X</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> will do the <NOBR>following:<SCRIPT>create_link(25);</SCRIPT>

⌨️ 快捷键说明

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