📄 ec2.htm
字号:
Finally, <CODE>X</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> will do the <NOBR>following:<SCRIPT>create_link(50);</SCRIPT>
</NOBR></p>
<A NAME="29204"></A><OL TYPE="1"><A NAME="dingp51"></A><LI>Call the standard <CODE>set_new_handler</CODE> with <CODE>X</CODE>'s error-handling
function. This will install <CODE>X</CODE>'s new-handler as the global new-
handler. In the code below, notice how you explicitly reference
the <CODE>std</CODE> scope (where the standard <CODE>set_new_handler</CODE> resides)
by using the "<CODE>::</CODE>" notation.<SCRIPT>create_link(51);</SCRIPT>
<A NAME="29220"></A><A NAME="dingp52"></A><LI>Call the global <CODE>operator</CODE> <CODE>new</CODE> to actually allocate the requested memory. If the initial attempt at allocation fails, the global <CODE>operator</CODE> <CODE>new</CODE> will invoke <CODE>X</CODE>'s new-handler, because that function was just installed as the global new-handler. If the global <CODE>operator</CODE> <CODE>new</CODE> is ultimately unable to find a way to allocate the requested memory, it will throw a <CODE>std::bad_alloc</CODE> exception, which <CODE>X</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> will catch. <CODE>X</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> will then restore the global new-handler that was originally in place, and it will return by propagating the exception.<SCRIPT>create_link(52);</SCRIPT>
<A NAME="29259"></A><A NAME="dingp53"></A><LI>Assuming the global <CODE>operator</CODE> <CODE>new</CODE> was able to successfully allocate enough memory for an object of type <CODE>X</CODE>, <CODE>X</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> will again call the standard <CODE>set_new_handler</CODE> to restore the global error-handling function to what it was originally. It will then return a pointer to the allocated memory.<SCRIPT>create_link(53);</SCRIPT>
</OL>
<A NAME="29254"></A>
<A NAME="dingp54"></A>Here's how you say all that in <NOBR>C++:<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<A NAME="1936"></A>
<UL><PRE>
void * X::operator new(size_t size)
{
new_handler globalHandler = // install X's
std::set_new_handler(currentHandler); // handler
</PRE>
</UL><A NAME="13310"></A>
<UL><PRE>
void *memory;
</PRE>
</UL><A NAME="29189"></A>
<UL><PRE>
try { // attempt
memory = ::operator new(size); // allocation
}
catch (std::bad_alloc&) { // restore
std::set_new_handler(globalHandler); // handler;
throw; // propagate
} // exception
</PRE>
</UL><A NAME="13313"></A>
<UL><PRE><A NAME="p31"></A>
std::set_new_handler(globalHandler); // restore
// handler
return memory;
}
</PRE>
</UL><A NAME="13317"></A>
<P><A NAME="dingp55"></A>
If the duplicated calls to <CODE>std::set_new_handler</CODE> caught your eye, turn to <A HREF="../MEC/MC3_FR.HTM#5292" TARGET="_top">Item M9</A> for information on how to eliminate <NOBR>them.<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>
<A NAME="222012"></A>
<P><A NAME="dingp56"></A>
Clients of class <CODE>X</CODE> use its new-handling capabilities like <NOBR>this:<SCRIPT>create_link(56);</SCRIPT>
</NOBR></P>
<A NAME="13319"></A>
<UL><PRE>
void noMoreMemory(); // decl. of function to
// call if memory allocation
// for X objects fails
</PRE>
</UL><A NAME="13320"></A>
<UL><PRE>
X::set_new_handler(noMoreMemory);
// set noMoreMemory as X's
// new-handling function
</PRE>
</UL><A NAME="13321"></A>
<UL><PRE>
X *px1 = new X; // if memory allocation
// fails, call noMoreMemory
</PRE>
</UL><A NAME="13328"></A>
<UL><PRE>
string *ps = new string; // if memory allocation
// fails, call the global
// new-handling function
// (if there is one)
</PRE>
</UL><A NAME="13322"></A>
<UL><PRE>
X::set_new_handler(0); // set the X-specific
// new-handling function
// to nothing (i.e., null)
</PRE>
</UL><A NAME="13323"></A>
<UL><PRE>
X *px2 = new X; // if memory allocation
// fails, throw an exception
// immediately. (There is
// no new-handling function
// for class X.)
</PRE>
</UL><A NAME="1938"></A>
<P><A NAME="dingp57"></A>
You may note that the code for implementing this scheme is the same regardless of the class, so a reasonable inclination would be to reuse it in other places. As <A HREF="./EC6_FR.HTM#7611" TARGET="_top">Item 41</A> explains, both inheritance and templates can be used to create reusable code. However, in this case, it's a combination of the two that gives you what you <NOBR>need.<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>
<A NAME="13352"></A>
<P><A NAME="dingp58"></A>
All you have to do is create a "mixin-style" base class, i.e., a base class that's designed to allow derived classes to inherit a single specific capability — in this case, the ability to set a class-specific new-handler. Then you turn the base class into a template. The base class part of the design lets derived classes inherit the <CODE>set_new_handler</CODE> and <CODE>operator</CODE> <CODE>new</CODE> functions they all need, while the template part of the design ensures that each inheriting class gets a different <CODE>currentHandler</CODE> data member. The result may sound a little complicated, but you'll find that the code looks reassuringly familiar. In fact, about the only real difference is that it's now reusable by any class that wants <NOBR>it:<SCRIPT>create_link(58);</SCRIPT>
</NOBR></P>
<A NAME="13379"></A>
<UL><PRE><A NAME="p32"></A>
template<class T> // "mixin-style" base class
class NewHandlerSupport { // for class-specific
public: // set_new_handler support
</PRE>
</UL><A NAME="13414"></A>
<UL><PRE>
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);
</PRE>
</UL><A NAME="13385"></A>
<UL><PRE>private:
static new_handler currentHandler;
};
</PRE>
</UL><A NAME="13404"></A>
<UL><PRE>template<class T>
new_handler NewHandlerSupport<T>::set_new_handler(new_handler p)
{
new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
</PRE>
</UL><A NAME="29269"></A>
<UL><PRE>template<class T>
void * NewHandlerSupport<T>::operator new(size_t size)
{
new_handler globalHandler =
std::set_new_handler(currentHandler);
</PRE>
</UL><A NAME="29270"></A>
<UL><PRE> void *memory;
</PRE>
</UL><A NAME="29281"></A>
<UL><PRE> try {
memory = ::operator new(size);
}
catch (std::bad_alloc&) {
std::set_new_handler(globalHandler);
throw;
}
</PRE>
</UL><A NAME="13421"></A>
<UL><PRE> std::set_new_handler(globalHandler);
</PRE>
</UL><A NAME="29286"></A>
<UL><PRE> return memory;
}
</PRE>
</UL><A NAME="31667"></A>
<UL><PRE>// this sets each currentHandler to 0
template<class T>
new_handler NewHandlerSupport<T>::currentHandler;
</PRE>
</UL><A NAME="13401"></A>
<P><A NAME="dingp59"></A>
With this class template, adding <CODE>set_new_handler</CODE> support to class <CODE>X</CODE> is easy: <CODE>X</CODE> just inherits from <CODE>newHandlerSupport<X></CODE>:<SCRIPT>create_link(59);</SCRIPT>
</P>
<A NAME="13443"></A>
<UL><PRE>// note inheritance from mixin base class template. (See
// <A HREF="../MAGAZINE/CO_FRAME.HTM" TARGET="_top">my article on counting objects</a> for information on why
// private inheritance might be preferable here.)
class X: public NewHandlerSupport<X> {
</PRE>
</UL><A NAME="13444"></A>
<UL><PRE>
... // as before, but no declarations for
}; // set_new_handler or operator new
</PRE>
</UL><A NAME="13446"></A>
<P><A NAME="dingp60"></A>
<A NAME="p33"></A>Clients of <CODE>X</CODE> remain oblivious to all the behind-the-scenes action; their old code continues to work. This is good, because one thing you can usually rely on your clients being is <NOBR>oblivious.<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P>
<A NAME="13491"></A>
<P><A NAME="dingp61"></A>
Using <CODE>set_new_handler</CODE> is a convenient, easy way to cope with the possibility of out-of-memory conditions. Certainly it's a lot more attractive than wrapping every use of <CODE>new</CODE> inside a <CODE>try</CODE> block. Furthermore, templates like <CODE>NewHandlerSupport</CODE> make it simple to add a class-specific new-handler to any class that wants one. Mixin-style inheritance, however, invariably leads to the topic of multiple inheritance, and before starting down that slippery slope, you'll definitely want to read <A HREF="./EC6_FR.HTM#7778" TARGET="_top">Item 43</A>.<SCRIPT>create_link(61);</SCRIPT>
</P>
<A NAME="13563"></A>
<P><A NAME="dingp62"></A>
Until 1993, C++ required that <CODE>operator</CODE> <CODE>new</CODE> return 0 when it was unable to satisfy a memory request. The current behavior is for <CODE>operator</CODE> <CODE>new</CODE> to throw a <CODE>std::bad_alloc</CODE> exception, but a lot of C++ was written before compilers began supporting the revised specification. The <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver = "self.status = 'ISO ANSI Standardization Committee'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standardization committee</A> didn't want to abandon the established test-for-0 code base, so they provided alternative forms of <CODE>operator</CODE> <CODE>new</CODE> (and <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE> — see <A HREF="#120851">Item 8</A>) that continue to offer the traditional failure-yields-0 behavior. These forms are called "nothrow" forms because, well, they never do a <CODE>throw</CODE>, and they employ <CODE>nothrow</CODE> objects (defined in the standard header <CODE><new></CODE>) at the point where <CODE>new</CODE> is <NOBR>used:<SCRIPT>create_link(62);</SCRIPT>
</NOBR></P>
<A NAME="13763"></A>
<UL><PRE>class Widget { ... };
</PRE>
</UL><A NAME="31673"></A>
<UL><PRE>
Widget *pw1 = new Widget; // throws std::bad_alloc if
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -