📄 mi9.htm
字号:
</NOBR></P>
<A NAME="38503"></A>
<P><A NAME="dingp11"></A>We don't have to if we can somehow move the cleanup code that must always be executed into the destructor for an object local to <CODE>processAdoptions</CODE>. That's because local objects are always destroyed when leaving a function, regardless of how that function is exited. (The only exception to this rule is when you call <CODE>longjmp</CODE>, and this shortcoming of <CODE>longjmp</CODE> is the primary reason why C++ has support for exceptions in the first place.) Our real concern, then, is moving the <CODE>delete</CODE> from <CODE>processAdoptions</CODE> into a destructor for an object local to <CODE>processAdoptions</CODE>.<SCRIPT>create_link(11);</SCRIPT>
</P><A NAME="38535"></A>
<P><A NAME="dingp12"></A>The solution is to replace the pointer <CODE>pa</CODE> with an object that <I>acts like</I> a pointer. That way, when the pointer-like object is (automatically) destroyed, we can have its destructor call <CODE>delete</CODE>. Objects that act like pointers, but do more, are called <I>smart pointers</I>, and, as <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A> explains, you can make pointer-like objects very smart indeed. In this case, we don't need a particularly brainy pointer, we just need a <A NAME="p48"></A>pointer-like object that knows enough to delete what it points to when the pointer-like object goes out of <NOBR>scope.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P><A NAME="69374"></A>
<P><A NAME="dingp13"></A>It's not difficult to write a class for such objects, but we don't need to. The standard C++ library (see <a href="../EC/EI49_FR.HTM#8392" TARGET="_top">Item E49</A>) contains a class template called <CODE>auto_ptr</CODE> that does just what we want. Each <CODE>auto_ptr</CODE> class takes a pointer to a heap object in its constructor and deletes that object in its destructor. Boiled down to these essential functions, <CODE>auto_ptr</CODE> looks like <NOBR>this:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></p><A NAME="69389"></A>
<UL><PRE>template<class T>
class auto_ptr {
public:
auto_ptr(T *p = 0): ptr(p) {} // save ptr to object
~auto_ptr() { delete ptr; } // delete ptr to object
</PRE>
</UL><A NAME="69421"></A>
<UL><PRE>private:
T *ptr; // raw ptr to object
};
</PRE>
</UL><A NAME="69425"></A>
<P><A NAME="dingp14"></A>The standard version of <CODE>auto_ptr</CODE> is much fancier, and this stripped-down implementation isn't suitable for real use<a href="#74869"><sup>3</sup></A> (we must add at least the copy constructor, assignment operator, and pointer-emulating functions discussed in <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A>), but the concept behind it should be clear: use <CODE>auto_ptr</CODE> objects instead of raw pointers, and you won't have to worry about heap objects not being deleted, not even when exceptions are thrown. (Because the <CODE>auto_ptr</CODE> destructor uses the single-object form of <CODE>delete</CODE>, <CODE>auto_ptr</CODE> is not suitable for use with pointers to <I>arrays</I> of objects. If you'd like an <CODE>auto_ptr</CODE>-like template for arrays, you'll have to write your own. In such cases, however, it's often a better design decision to use a <CODE>vector</CODE> instead of an array, <NOBR>anyway.)<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="69464"></A>
<P><A NAME="dingp15"></A>Using an <CODE>auto_ptr</CODE> object instead of a raw pointer, <CODE>processAdoptions</CODE> looks like <NOBR>this:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="69466"></A>
<UL><PRE>void processAdoptions(istream& dataSource)
{
while (dataSource) {
auto_ptr<ALA> pa(readALA(dataSource));
pa->processAdoption();
}
}
</PRE>
</UL>
<A NAME="79353"></A>
<P><A NAME="dingp16"></A>This version of <CODE>processAdoptions </CODE>differs from the original in only two ways. First, <CODE>pa</CODE> is declared to be an <CODE>auto_ptr<ALA></CODE> object, not a raw <CODE>ALA*</CODE> pointer. Second, there is no <CODE>delete</CODE> statement at the end of the loop. That's it. Everything else is identical, because, except for destruction, <CODE>auto_ptr</CODE> objects act just like normal pointers. Easy, <NOBR>huh?<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="79324"></A>
<A NAME="p49"></A>
<P><A NAME="dingp17"></A>The idea behind <CODE>auto_ptr</CODE> — using an object to store a resource that needs to be automatically released and relying on that object's destructor to release it — applies to more than just pointer-based resources. Consider a function in a GUI application that needs to create a window to display some <NOBR>information:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="43844"></A>
<UL><PRE>// this function may leak resources if an exception
// is thrown
void displayInfo(const Information& info)
{
WINDOW_HANDLE w(createWindow());
<A NAME="43845"></A>
<I>display info in window corresponding to w;</I>
<A NAME="43803"></A>
destroyWindow(w);
}
</PRE>
</UL><A NAME="43798"></A>
<P><A NAME="dingp18"></A>Many window systems have C-like interfaces that use functions like <CODE>createWindow</CODE> and <CODE>destroyWindow</CODE> to acquire and release window resources. If an exception is thrown during the process of displaying <CODE>info</CODE> in <CODE>w</CODE>, the window for which <CODE>w</CODE> is a handle will be lost just as surely as any other dynamically allocated <NOBR>resource.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P><A NAME="43903"></A>
<P><A NAME="dingp19"></A>The solution is the same as it was before. Create a class whose constructor and destructor acquire and release the <NOBR>resource:<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="43811"></A>
<UL><PRE>// class for acquiring and releasing a window handle
class WindowHandle {
public:
WindowHandle(WINDOW_HANDLE handle): w(handle) {}
~WindowHandle() { destroyWindow(w); }
</PRE>
</UL><A NAME="43814"></A>
<UL><PRE> operator WINDOW_HANDLE() { return w; } // see below
</PRE>
</UL><A NAME="57440"></A>
<UL><PRE>private:
WINDOW_HANDLE w;
</PRE>
</UL><A NAME="69531"></A>
<UL><PRE>
// The following functions are declared private to prevent
// multiple copies of a WINDOW_HANDLE from being created.
// See <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A> for a discussion of a more flexible approach.
WindowHandle(const WindowHandle&);
WindowHandle& operator=(const WindowHandle&);
};
</PRE>
</UL><A NAME="69562"></A>
<P><A NAME="dingp20"></A>This looks just like the <CODE>auto_ptr</CODE> template, except that assignment and copying are explicitly prohibited (see <a href="../EC/EI27_FR.HTM#6406" TARGET="_top">Item E27</A>), and there is an implicit conversion operator that can be used to turn a <CODE>WindowHandle</CODE> into a <CODE>WINDOW_HANDLE</CODE>. This capability is essential to the practical application of a <CODE>WindowHandle</CODE> object, because it means you can use a <CODE>WindowHandle</CODE> just about anywhere you would normally use a raw <CODE>WINDOW_HANDLE</CODE>. (See <a href="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>, however, for why you should generally be leery of implicit type conversion <NOBR>operators.)<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P><A NAME="69589"></A>
<A NAME="p50"></A>
<P><A NAME="dingp21"></A>Given the <CODE>WindowHandle</CODE> class, we can rewrite <CODE>displayInfo</CODE> as <NOBR>follows:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="43827"></A>
<UL><PRE>// this function avoids leaking resources if an
// exception is thrown
void displayInfo(const Information& info)
{
WindowHandle w(createWindow());
<A NAME="43867"></A>
<I>display info in window corresponding to w;</I>
<A NAME="43836"></A>
}
</PRE>
</UL><A NAME="69593"></A>
<P><A NAME="dingp22"></A>Even if an exception is thrown within <CODE>displayInfo</CODE>, the window created by <CODE>createWindow</CODE> will always be <NOBR>destroyed.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P><A NAME="43780"></A>
<P><A NAME="dingp23"></A>By adhering to the rule that resources should be encapsulated inside objects, you can usually avoid resource leaks in the presence of exceptions. But what happens if an exception is thrown while you're in the process of acquiring a resource, e.g., while you're in the constructor of a resource-acquiring class? What happens if an exception is thrown during the automatic destruction of such resources? Don't constructors and destructors call for special techniques? They do, and you can read about them in Items <a href="./MI10_FR.HTM#38223" TARGET="_top">10</A> and <a href="./MI11_FR.HTM#39749" TARGET="_top">11</A>.<SCRIPT>create_link(23);</SCRIPT>
</p>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MEXCEPFR.HTM" TARGET="_top">Exceptions</A> <BR> Continue to <A HREF="./MI10_FR.HTM" TARGET="_top">Item 10: Prevent resource leaks in constructors</A></FONT></DIV>
<A NAME="74869"></A>
<HR WIDTH="100%">
<A NAME="dingp24"></A>
<SUP>3</SUP> A complete version of an almost-standard <CODE>auto_ptr</CODE> appears on pages <A HREF="./MIAUTOFR.HTM#p291" TARGET="_top">291</A>-<A HREF="./MIAUTOFR.HTM#p294" TARGET="_top">294</A>.<SCRIPT>create_link(24);</SCRIPT>
<BR>
<A HREF="#69425">Return</A>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -