📄 ei8.htm
字号:
<!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 8: Adhere to convention when writing operator new and operator delete.</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 = "EI8_DIR.HTM";
var dingtext = "Item E8, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E8: Conventions for operators new and delete" --><A NAME="128051"></A>
<A NAME="120851"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="EI7_FR.HTM" TARGET="_top">Item 7: Be prepared for out-of-memory conditions. </A> <BR> Continue to <A HREF="EI9_FR.HTM" TARGET="_top">Item 9: Avoid hiding the "normal" form of new.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 8: Adhere to convention when writing <CODE>operator</CODE>
<CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE>.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="120857"></A>
<P><A NAME="dingp2"></A>
When you take it upon yourself to write <CODE>operator</CODE> <CODE>new</CODE> (<A HREF="./EI10_FR.HTM#1986" TARGET="_top">Item 10</A> explains why you might want to), it's important that your function(s) <A NAME="p34"></A>offer behavior that is consistent with the default <CODE>operator</CODE> <CODE>new</CODE>. In practical terms, this means having the right return value, calling an error-handling function when insufficient memory is available (see <A HREF="./EI7_FR.HTM#1894" TARGET="_top">Item 7</A>), and being prepared to cope with requests for no memory. You also need to avoid inadvertently hiding the "normal" form of <CODE>new</CODE>, but that's a topic for <A HREF="./EI9_FR.HTM#1961" TARGET="_top">Item 9</A>.<SCRIPT>create_link(2);</SCRIPT>
</P>
<A NAME="13550"></A>
<P><A NAME="dingp3"></A>
The return value part is easy. If you can supply the requested memory, you just return a pointer to it. If you can't, you follow the rule described in <A HREF="./EI7_FR.HTM#1894" TARGET="_top">Item 7</A> and throw an exception of type <CODE>std::bad_alloc</CODE>.<SCRIPT>create_link(3);</SCRIPT>
</P>
<A NAME="13554"></A>
<P><A NAME="dingp4"></A>
It's not quite that simple, however, because <CODE>operator</CODE> <CODE>new</CODE> actually tries to allocate memory more than once, calling the error-handling function after each failure, the assumption being that the error-handling function might be able to do something to free up some memory. Only when the pointer to the error-handling function is null does <CODE>operator</CODE> <CODE>new</CODE> throw an <NOBR>exception.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="13672"></A>
<P><A NAME="dingp5"></A>
In addition, the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver = "self.status = 'The latest publicly-available version of the C++ standard'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standard</A> requires that <CODE>operator</CODE> <CODE>new</CODE> return a legitimate pointer even when 0 bytes are requested. (Believe it or not, requiring this odd-sounding behavior actually simplifies things elsewhere in the <NOBR>language.)<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="1944"></A>
<P><A NAME="dingp6"></A>
That being the case, pseudocode for a non-member <CODE>operator</CODE> <CODE>new</CODE> looks like <NOBR>this:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="1946"></A>
<UL><PRE>
void * operator new(size_t size) // your operator new might
{ // take additional params
</PRE>
</UL><A NAME="13653"></A>
<UL><PRE>
if (size == 0) { // handle 0-byte requests
size = 1; // by treating them as
} // 1-byte requests
</PRE>
</UL><A NAME="13652"></A>
<UL><PRE> while (1) {
<i>attempt to allocate</i> <CODE>size</CODE> <i>bytes</i>;
</PRE>
</UL><A NAME="1948"></A>
<UL><PRE> if (<i>the allocation was successful</i>)
return (<i>a pointer to the memory</i>);
</PRE>
</UL><A NAME="1947"></A>
<UL><PRE>
// allocation was unsuccessful; find out what the
// current error-handling function is (see <A HREF="./EI7_FR.HTM#1894" TARGET="_top">Item 7</A>)
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
</PRE>
</UL><A NAME="13687"></A>
<UL><PRE> if (globalHandler) (*globalHandler)();
elsethrow std::bad_alloc();
}
}
</PRE>
</UL><A NAME="13688"></A>
<P><A NAME="dingp7"></A>
<A NAME="p35"></A>The trick of treating requests for zero bytes as if they were really requests for one byte looks slimy, but it's simple, it's legal, it works, and how often do you expect to be asked for zero bytes, <NOBR>anyway?<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="13654"></A>
<P><A NAME="dingp8"></A>
You may also look askance at the place in the pseudocode where the error-handling function pointer is set to null, then promptly reset to what it was originally. Unfortunately, there is no way to get at the error-handling function pointer directly, so you have to call <CODE>set_new_handler</CODE> to find out what it is. Crude, yes, but also <NOBR>effective.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="29835"></A>
<P><A NAME="dingp9"></A>
<A HREF="./EI7_FR.HTM#1894" TARGET="_top">Item 7</A> remarks that <CODE>operator</CODE> <CODE>new</CODE> contains an infinite loop, and the code above shows that loop explicitly — <CODE>while</CODE> <CODE>(1)</CODE> is about as infinite as it gets. The only way out of the loop is for memory to be successfully allocated or for the new-handling function to do one of the things described in <A HREF="./EI7_FR.HTM#1894" TARGET="_top">Item 7</A>: make more memory available, install a different new-handler, deinstall the new-handler, throw an exception of or derived from <CODE>std::bad_alloc</CODE>, or fail to return. It should now be clear why the new-handler must do one of those things. If it doesn't, the loop inside <CODE>operator</CODE> <CODE>new</CODE> will never <NOBR>terminate.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="1951"></A>
<P><A NAME="dingp10"></A>
One of the things many people don't realize about <CODE>operator</CODE> <CODE>new</CODE> is that it's inherited by subclasses. That can lead to some interesting complications. In the pseudocode for <CODE>operator</CODE> <CODE>new</CODE> above, notice that the function tries to allocate <CODE>size</CODE> bytes (unless <CODE>size</CODE> is 0). That makes perfect sense, because that's the argument that was passed to the function. However, most class-specific versions of <CODE>operator</CODE> <CODE>new</CODE> (including the one you'll find in <A HREF="./EI10_FR.HTM#1986" TARGET="_top">Item 10</A>) are designed for a <I>specific</I> class, <I>not</I> for a class <I>or</I> any of its subclasses. That is, given an <CODE>operator</CODE> <CODE>new</CODE> for a class <CODE>X</CODE>, the behavior of that function is almost always carefully tuned for objects of size <CODE>sizeof(X)</CODE> — nothing larger and nothing smaller. Because of inheritance, however, it is possible that the <CODE>operator</CODE> <CODE>new</CODE> in a base class will be called to allocate memory for an object of a derived <NOBR>class:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="1952"></A>
<UL><PRE>class Base {
public:
static void * operator new(size_t size);
...
};
</PRE>
</UL><A NAME="1953"></A>
<UL><PRE>
class Derived: public Base // Derived doesn't declare
{ ... }; // operator new
</PRE>
</UL><A NAME="1954"></A>
<UL><PRE>
Derived *p = new Derived; // calls Base::operator new!
</PRE>
</UL><A NAME="1955"></A>
<P><A NAME="dingp11"></A>
If <CODE>Base</CODE>'s class-specific <CODE>operator</CODE> <CODE>new</CODE> wasn't designed to cope with this — and chances are slim that it was — the best way for it to handle the <A NAME="p36"></A>situation is to slough off calls requesting the "wrong" amount of memory to the standard <CODE>operator</CODE> <CODE>new</CODE>, like <NOBR>this:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="1956"></A>
<UL><PRE>void * Base::operator new(size_t size)
{
if (size != sizeof(Base)) // if size is "wrong,"
return ::operator new(size); // have standard operator
// new handle the request
</PRE>
</UL><A NAME="1957"></A>
<UL><PRE>
... // otherwise handle
// the request here
}
</PRE>
</UL><A NAME="13661"></A>
<P><A NAME="dingp12"></A>
"Hold on!" I hear you cry, "You forgot to check for the pathological-but-nevertheless-possible case where <CODE>size</CODE> is zero!" Actually, I didn't, and please stop using hyphens when you cry out. The test is still there, it's just been incorporated into the test of <CODE>size</CODE> against <CODE>sizeof(Base)</CODE>. The <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver = "self.status = 'The latest publicly-available version of the C++ standard'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standard</A> works in mysterious ways, and one of those ways is to decree that all freestanding classes have nonzero size. By definition, <CODE>sizeof(Base)</CODE> can never be zero (even if it has no members), so if <CODE>size</CODE> is zero, the request will be forwarded to <CODE>::operator</CODE> <CODE>new</CODE>, and it will become that function's responsibility to treat the request in a reasonable fashion. (Interestingly, <CODE>sizeof(Base)</CODE> may be zero if <CODE>Base</CODE> is not a freestanding class. For details, consult <A HREF="../MAGAZINE/CO_FRAME.HTM" TARGET="_top">my article on counting objects</A>.)<SCRIPT>create_link(12);</SCRIPT>
</P>
<A NAME="3358"></A>
<P><A NAME="dingp13"></A>
If you'd like to control memory allocation for arrays on a per-class basis, you need to implement <CODE>operator</CODE> <CODE>new</CODE>'s array-specific cousin, <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE>. (This function is usually called "array new," because it's hard to figure out how to pronounce "operator <NOBR>new[]</NOBR>".) If you decide to write <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE>, remember that all you're doing is allocating raw memory — you can't do anything to the as-yet-nonexistent objects in the array. In fact, you can't even figure out how many objects will be in the array, because you don't know how big each object is. After all, a base class's <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE> might, through inheritance, be called to allocate memory for an array of derived class objects, and derived class objects are usually bigger than base class objects. Hence, you can't assume inside <CODE>Base::operator</CODE> <CODE><NOBR>new[]</NOBR></CODE> that the size of each object going into the array is <CODE>sizeof(Base)</CODE>, and that means you can't assume that the number of objects in the array is <CODE>(</CODE><CODE><I>bytes</I></CODE> <CODE><I>requested</I></CODE><CODE>)/sizeof(Base)</CODE>. For more information on <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE>, see <A HREF="../MEC/MI8_FR.HTM#33985" TARGET="_top">Item M8</A>.<SCRIPT>create_link(13);</SCRIPT>
</P>
<A NAME="13595"></A>
<P><A NAME="dingp14"></A>
So much for the conventions you need to follow when writing <CODE>operator</CODE> <CODE>new</CODE> (and <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE>). For <CODE>operator</CODE> <CODE>delete</CODE> (and its array counterpart, <CODE>operator</CODE> <CODE><NOBR>delete[]</NOBR></CODE>), things are simpler. About all you need to remember is that C++ guarantees it's always safe to delete the null pointer, so you need to honor that guarantee. Here's pseudocode for a non-member <CODE>operator delete</CODE>:<SCRIPT>create_link(14);</SCRIPT>
</P>
<A NAME="13607"></A>
<UL><PRE><A NAME="p37"></A>void operator delete(void *rawMemory)
{
if (rawMemory == 0) return; // do nothing if the null
// pointer is being deleted
</PRE>
</UL><A NAME="13615"></A>
<UL><PRE> <I>deallocate the memory pointed to by</I> rawMemory;
</PRE>
</UL><A NAME="13616"></A>
<UL><PRE> return;
}
</PRE>
</UL><A NAME="13619"></A>
<P><A NAME="dingp15"></A>
The member version of this function is simple, too, except you've got to be sure to check the size of what's being deleted. Assuming your class-specific <CODE>operator</CODE> <CODE>new</CODE> forwards requests of the "wrong" size to <CODE>::operator</CODE> <CODE>new</CODE>, you've got to forward "wrongly sized" deletion requests to <CODE>::operator</CODE> <CODE>delete</CODE>:<SCRIPT>create_link(15);</SCRIPT>
</P>
<A NAME="13622"></A>
<UL><PRE>
class Base { // same as before, but now
public: // op. delete is declared
static void * operator new(size_t size);
static void operator delete(void *rawMemory, size_t size);
...
};
</PRE>
</UL><A NAME="13632"></A>
<UL><PRE>void Base::operator delete(void *rawMemory, size_t size)
{
if (rawMemory == 0) return; // check for null pointer
</PRE>
</UL><A NAME="13635"></A>
<UL><PRE>
if (size != sizeof(Base)) { // if size is "wrong,"
::operator delete(rawMemory); // have standard operator
return; // delete handle the request
}
</PRE>
</UL><A NAME="13633"></A>
<UL><PRE> <I>deallocate the memory pointed to by</I> rawMemory;
</PRE>
</UL><A NAME="13627"></A>
<UL><PRE> return;
}
</PRE>
</UL><A NAME="13640"></A>
<P><A NAME="dingp16"></A>
The conventions, then, for <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> (and their array counterparts) are not particularly onerous, but it is important that you obey them. If your allocation routines support new-handler functions and correctly deal with zero-sized requests, you're all but finished, and if your deallocation routines cope with null pointers, there's little more to do. Add support for inheritance in member versions of the functions, and <I>presto!</I> — you're <NOBR>done.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="EI7_FR.HTM" TARGET="_top">Item 7: Be prepared for out-of-memory conditions. </A> <BR> Continue to <A HREF="EI9_FR.HTM" TARGET="_top">Item 9: Avoid hiding the "normal" form of new.</A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -