📄 ei10.htm
字号:
<A NAME="14387"></A>
<P><A NAME="dingp28"></A>
Nevertheless, the blocks of memory returned by <CODE>::operator</CODE> <CODE>new</CODE> are never released by <CODE>Airplane::operator</CODE> <CODE>delete</CODE>, and there has to be <I>some</I> name for that. There is. You've created a memory <I>pool</I>. Call it semantic gymnastics if you must, but there is an important difference between a memory leak and a memory pool. A memory leak may grow indefinitely, even if clients are well-behaved, but a memory pool never grows larger than the maximum amount of memory requested by its <NOBR>clients.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P>
<A NAME="14410"></A>
<P><A NAME="dingp29"></A>
It would not be difficult to modify <CODE>Airplane</CODE>'s memory management routines so that the blocks of memory returned by <CODE>::operator</CODE> <CODE>new</CODE> were automatically released when they were no longer in use, but there are two reasons why you might not want to do <NOBR>it.<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>
<A NAME="14421"></A>
<P><A NAME="dingp30"></A>
The first concerns your likely motivation for tackling custom memory management. There are many reasons why you might do it, but the most common one is that you've determined (see <A HREF="../MEC/MI16_FR.HTM#40995" TARGET="_top">Item M16</A>) that the default <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> use too much memory or are too slow (or both). That being the case, every additional byte and every additional statement you devote to tracking and releasing those big memory blocks comes straight off the bottom line: your software runs slower and uses more memory than it would if you adopted the pool strategy. For libraries and applications in which performance is at <A NAME="p46"></A>a premium and you can expect pool sizes to be reasonably bounded, the pool approach may well be <NOBR>best.<SCRIPT>create_link(30);</SCRIPT>
</NOBR></P>
<A NAME="14482"></A>
<P><A NAME="dingp31"></A>
The second reason has to do with pathological behavior. Suppose <CODE>Airplane</CODE>'s memory management routines are modified so <CODE>Airplane</CODE>'s <CODE>operator</CODE> <CODE>delete</CODE> releases any big block of memory that has no active objects in it. Now consider this <NOBR>program:<SCRIPT>create_link(31);</SCRIPT>
</NOBR></P>
<A NAME="14497"></A>
<UL><PRE>int main()
{
Airplane *pa = new Airplane; // first allocation: get big
// block, make free list, etc.
</PRE>
</UL><A NAME="14498"></A>
<UL><PRE>
delete pa; // block is now empty;
// release it
</PRE>
</UL><A NAME="14499"></A>
<UL><PRE>
pa = new Airplane; // uh oh, get block again,
// make free list, etc.
</PRE>
</UL><A NAME="14500"></A>
<UL><PRE>
delete pa; // okay, block is empty
// again; release it
</PRE>
</UL><A NAME="14501"></A>
<UL><PRE>
... // you get the idea...
</PRE>
</UL><A NAME="14502"></A>
<UL><PRE> return 0;
}
</PRE>
</UL><A NAME="14362"></A>
<P><A NAME="dingp32"></A>
This nasty little program will run slower and use more memory than with even the <I>default</I> <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE>, much less the pool-based versions of those <NOBR>functions!<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>
<A NAME="14524"></A>
<P><A NAME="dingp33"></A>
Of course, there are ways to deal with this pathology, but the more you code for uncommon special cases, the closer you get to reimplementing the default memory management functions, and then what have you gained? A memory pool is not the answer to all memory management questions, but it's a reasonable answer to many of <NOBR>them.<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P>
<A NAME="14528"></A>
<P><A NAME="dingp34"></A>
In fact, it's a reasonable answer often enough that you may be bothered by the need to reimplement it for different classes. "Surely," you think to yourself, "there should be a way to package the notion of a fixed-sized memory allocator so it's easily reused." There is, though this Item has droned on long enough that I'll leave the details in the form of the dreaded exercise for the <NOBR>reader.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<A NAME="14882"></A>
<P><A NAME="dingp35"></A>
Instead, I'll simply show a minimal interface (see <A HREF="./EI18_FR.HTM#17774" TARGET="_top">Item 18</A>) to a <CODE><I>Pool</I></CODE> class, where each object of type <CODE>Pool</CODE> is an allocator for objects of the size specified in the <CODE>Pool</CODE>'s <NOBR>constructor:<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P>
<A NAME="14550"></A>
<UL><PRE>class Pool {
public:
Pool(size_t n); // Create an allocator for
// objects of size n
</PRE>
</UL><A NAME="14551"></A>
<UL><PRE><A NAME="p47"></A>
void * alloc(size_t n) ; // Allocate enough memory
// for one object; follow
// operator new conventions
// from <A HREF="./EI8_FR.HTM#120851" TARGET="_top">Item 8</A>
</PRE>
</UL><A NAME="14637"></A>
<UL><PRE>
void free( void *p, size_t n); // Return to the pool the
// memory pointed to by p;
// follow operator delete
// conventions from <A HREF="./EI8_FR.HTM#120851" TARGET="_top">Item 8</A>
</PRE>
</UL><A NAME="14638"></A>
<UL><PRE>
~Pool(); // Deallocate all memory in
// the pool
};
</PRE>
</UL><A NAME="14581"></A>
<P><A NAME="dingp36"></A>
This class allows <CODE>Pool</CODE> objects to be created, to perform allocation and deallocation operations, and to be destroyed. When a <CODE>Pool</CODE> object is destroyed, it releases all the memory it allocated. This means there is now a way to avoid the memory leak-like behavior that <CODE>Airplane</CODE>'s functions exhibited. However, this also means that if a <CODE>Pool</CODE>'s destructor is called too soon (before all the objects using its memory have been destroyed), some objects will find their memory yanked out from under them before they're done using it. To say that the resulting behavior is undefined is being <NOBR>generous.<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P>
<A NAME="14600"></A>
<P><A NAME="dingp37"></A>
Given this <CODE>Pool</CODE> class, even a Java programmer can add custom memory management capabilities to <CODE>Airplane</CODE> without breaking a <NOBR>sweat:<SCRIPT>create_link(37);</SCRIPT>
</NOBR></P>
<A NAME="14606"></A>
<UL><PRE>class Airplane {
public:
</PRE>
</UL><A NAME="14608"></A>
<UL><PRE>
... // usual Airplane functions
</PRE>
</UL><A NAME="14635"></A>
<UL><PRE> static void * operator new(size_t size);
static void operator delete(void *p, size_t size);
</PRE>
</UL><A NAME="14636"></A>
<UL><PRE>private:
AirplaneRep *rep; // pointer to representation
static Pool memPool; // memory pool for Airplanes
</PRE>
</UL><A NAME="14609"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="14623"></A>
<UL><PRE>inline void * Airplane::operator new(size_t size)
{ return memPool.alloc(size); }
</PRE>
</UL><A NAME="14624"></A>
<UL><PRE>inline void Airplane::operator delete(void *p,
size_t size)
{ memPool.free(p, size); }
</PRE>
</UL><A NAME="14629"></A>
<UL><PRE>// create a new pool for Airplane objects; this goes in
// the class implementation file
Pool Airplane::memPool(sizeof(Airplane));
</PRE>
</UL><A NAME="3256"></A>
<P><A NAME="dingp38"></A>
This is a much cleaner design than the one we saw earlier, because the <CODE>Airplane</CODE> class is no longer cluttered with non-airplane details. Gone <A NAME="p48"></A>are the <CODE>union</CODE>, the head of the free list, the constant defining how big each raw memory block should be, etc. That's all hidden inside <CODE>Pool</CODE>, which is really where it should be. Let <CODE>Pool</CODE>'s author worry about memory management minutiae. Your job is to make the <CODE>Airplane</CODE> class work <NOBR>properly.<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="14669"></A>
<P><A NAME="dingp39"></A>
Now, it's interesting to see how custom memory management routines can improve program performance, and it's worthwhile to see how such routines can be encapsulated inside a class like <CODE>Pool</CODE>, but let us not lose sight of the main point. That point is that <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> need to work together, so if you write <CODE>operator</CODE> <CODE>new</CODE>, be sure to write <CODE>operator</CODE> <CODE>delete</CODE>, as <NOBR>well.<SCRIPT>create_link(39);</SCRIPT>
</NOBR></P>
<HR WIDTH="100%">
<A NAME="dingp40"></A><A NAME="14365"></A><SUP>4</SUP> I write this with certainty, because I failed to address this issue in the first edition of this book, and <I>many</I> readers upbraided me for the omission. There's nothing quite like a few thousand proofreaders to demonstrate one's fallibility, sigh.<SCRIPT>create_link(40);</SCRIPT>
<BR>
<A HREF="#14350">Return</A></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI9_FR.HTM" TARGET="_top">Item 9: Avoid hiding the "normal" form of new.</A> <BR> Continue to <A HREF="./ECTOR_FR.HTM" TARGET="_top">Constructors, Destructors, and Assignment Operators</A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -