📄 reeves.htm
字号:
~X();
};
X::X()
{
p1_ = new T1;
p2_ = new T2; // exception causes leak
}
</PRE>
</UL>
<P><A NAME="dingp92"></A><A NAME="AUTO00082"></A>If an exception is thrown by <CODE>T2()</CODE> during the initialization of <CODE>p2_</CODE>, then the <CODE>T2</CODE> object will be destroyed, and the memory obtained by new will be released, but not the pointer held in <CODE>p1_</CODE>. We have a memory <NOBR>leak.<SCRIPT>create_link(92);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp93"></A><A NAME="AUTO00083"></A>There are a couple of ways this can be dealt with. We could use a try block to catch the exception and attempt to release the memory, but if we have several resources allocated this way, then the nested try blocks can get tedious and error prone. An alternative is to make sure the pointers are initialized to null, and then delete them all in the catch <NOBR>block:<SCRIPT>create_link(93);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00157"></A><UL><PRE>
X::X() try :
p1_(null), p2_(null)
{
p1_ = new T1;
p2_ = new T2;
}
catch (...) {
delete p2_;
delete p1_; // reverse order
throw; // redundant
}
</PRE>
</UL>
<P><A NAME="dingp94"></A><A NAME="AUTO00084"></A>This example uses several features from the new Standard, so do not expect this to work on your compiler yet. Placing the <CODE>try</CODE> keyword immediately after the parameter list (or the exception specification if one exists), and the catch clauses after the function body, produces a <EM>function try block</EM>. A function try block associates a handler with the entire function body, including the <EM>constructor initializer list</EM>. In this example, the initializer list is not the concern, but you can see that the initializer list is within the scope of the <CODE>try</CODE>. With any pointer not set in the constructor body guaranteed to be null, the catch block can safely invoke delete on them. (The <CODE>throw</CODE> in the catch block is redundant. A catch clause of a function try block for a constructor or a destructor will automatically rethrow the exception when it finishes. As a matter of style, however, I always explicitly rethrow exceptions caught by a <CODE>catch</CODE> <CODE>(...)</CODE> clause (<A HREF="#guide10" ONMOUSEOVER = "self.status = 'Guideline 10'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Guideline 10</A>).) All this leads <NOBR>to:<SCRIPT>create_link(94);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp95"></A><A NAME="AUTO00085"></A><A NAME="guide6"></A><I>Guideline 6: If you have raw data pointers as members, initialize them to null in the initializer list of your constructor(s), then do necessary allocations in the constructor body where a <CODE>catch</CODE> block can deal with potential resource leaks.</I> This is one possible way to deal with a potential resource leak in a constructor. Another technique is to use the "resource acquisition is initialization" strategy. In this case, we make sure that every resource is associated with an object whose destructor will deallocate it. For dealing with memory allocated from the free store, the standard library template class <CODE>auto_ptr</CODE> (<A HREF="#list2" ONMOUSEOVER = "self.status = 'Listing 2'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Listing 2</A>) is available. Applied to our <NOBR>example:<SCRIPT>create_link(95);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00158"></A><UL><PRE>
class X {
auto_ptr<T1> ap1_;
auto_ptr<T2> ap2_;
public:
X();
~X() {};
};
X::X() :
ap1_(new T1),
ap2_(new T2)
{}
</PRE>
</UL>
<P><A NAME="dingp96"></A><A NAME="AUTO00086"></A>Alternatively:<SCRIPT>create_link(96);</SCRIPT>
<A NAME="AUTO00159"></A><UL><PRE>
X::X()
{
ap1_.reset(new T1);
ap2_.reset(new T2);
}
</PRE>
</UL>
<P><A NAME="dingp97"></A><A NAME="AUTO00087"></A>Now, since <CODE>ap1_</CODE> and <CODE>ap2_</CODE> are both objects, if an exception occurs trying to initialize <CODE>ap2_</CODE>, then the stack unwind will destroy <CODE>ap1_</CODE>, which will call delete on the allocated pointer. In this case, our destructor is empty since the destructors of member objects are invoked <NOBR>automatically.<SCRIPT>create_link(97);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp98"></A><A NAME="AUTO00088"></A>Besides having a destructor that will delete the resource, template <CODE>auto_ptr</CODE> also provides functionality for safely transferring ownership of a resource. We have made use of this capability several times already. In this case, we can use an "acquire then transfer ownership" strategy to give us the following version of <CODE>X</CODE>'s <NOBR>constructor:<SCRIPT>create_link(98);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00160"></A><UL><PRE>
class X {
T1* p1_;
T2* p2_;
public:
X();
~X() {delete p1_; delete p2_;}
};
X::X() :
p1_(), p2_()
{
auto_ptr<T1> t1(new T1);
auto_ptr<T2> t2(new T2);
p1_ = t1.release();
p2_ = t2.release();
}
</PRE>
</UL>
<P><A NAME="dingp99"></A><A NAME="AUTO00089"></A>The <CODE>auto_ptr</CODE> objects are used to acquire the resources in a manner that guarantees they will be deleted if an exception occurs. When all resources have been successfully acquired, ownership is transferred to the class itself. This is just <A HREF="#guide2" ONMOUSEOVER = "self.status = 'Guideline 2'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Guideline 2</A> applied to resource acquisition. I see this as a transitional strategy for constructors, however. In the long run, I suspect that the use of raw data pointers as class members will diminish in favor of <CODE>auto_ptr</CODE> style objects. This simplifies maintenance as well as the problems of coping with exceptions. If you worry about performance, keep in mind that all the operations of <CODE>auto_ptr</CODE> are inline functions (see <SCRIPT>sendmetoo(33,6729,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Item E33'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item E33</A>). Most of these are one line functions that any decent compiler should have no trouble <NOBR>handling.<SCRIPT>create_link(99);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp100"></A><A NAME="AUTO00090"></A>Resource allocations in ordinary functions can also cause resource leaks. Whereas a constructor is building an object and the goal is to make sure everything already acquired is released if an exception occurs, a function usually obtains a resource for internal use and releases it upon completion. If an exception happens after the resource is acquired but before it is released, then we have a leak. Under the discussion for <A HREF="#guide2" ONMOUSEOVER = "self.status = 'Guideline 2'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Guideline 2</A>, we used an <CODE>auto_array_ptr</CODE> object in <CODE>Stack::push()</CODE> to manage the new buffer while it was being initialized. We can do the same thing in <CODE>Stack::operator=()</CODE>:<SCRIPT>create_link(100);</SCRIPT>
</P>
<A NAME="AUTO00161"></A><UL><PRE>
template<class T>
Stack<T>& Stack::operator=(const Stack<T>& rhs)
{
if (this == &rhs) return *this;
auto_array_ptr<T> new_buffer =
new T[rhs.nelems_]; //>x1
for (int i = 0; i < rhs.top_; i++)
new_buffer[i] = rhs.v_[i]; //>x2
v_ = new_buffer.reset(v_); // swap ownership
nelems_ = rhs.nelems_;
top_ = rhs.top_;
return *this;
}
</PRE>
</UL>
<P><A NAME="dingp101"></A><A NAME="AUTO00091"></A>If <CODE>v_</CODE> were an <CODE>auto_array_ptr</CODE> object instead of a raw pointer we would use the Standard template function <CODE>swap()</CODE> to perform the exchange of <NOBR>ownership:<SCRIPT>create_link(101);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00162"></A><UL><PRE>
swap(v_, new_buffer);
</PRE>
</UL>
<P><A NAME="dingp102"></A><A NAME="AUTO00092"></A>Finally, we can have resource leaks in destructors. As a general rule, we do not want to throw (or propagate) exceptions from destructors (see <SCRIPT>sendmetoo(11,39749,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M11'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M11</A> and Herb Sutter's article on <A HREF="SU_FRAME.HTM" TARGET="_top" ONMOUSEOVER = "self.status = 'Exception-Safe Generic Containers'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">"Exception-Safe Generic Containers"</A>). Nevertheless, we can not always prevent it, so let us take a look at a simple problem. Consider our first example of class <CODE>X</CODE> above. We have two pointers to two different types of objects. Everything has gone well, and now the destructor of <CODE>X</CODE> is invoked. It is pretty <NOBR>simple.<SCRIPT>create_link(102);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00163"></A><UL><PRE>
X::~X() { delete p2_; delete p1_; }
</PRE>
</UL>
<P><A NAME="dingp103"></A><A NAME="AUTO00093"></A>Not much to go wrong here, but assume that it does — say the <CODE>T2</CODE> object throws an exception when deleted. Like constructors, destructors are special functions for the exception runtime mechanism. When an exception occurs in a destructor, it is treated like an exception in a constructor, i.e. all complete subobjects that still exist are destroyed (in reverse order of their construction) and then the memory deallocation function is called, if needed. (I assume this is what is suppose to happen. The Standard is not at all clear about how exceptions from destructors are handled. I make this assumption based upon the discussion of exceptions in the ARM (see <SCRIPT>sendmetoo(50,8569,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Item E50'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item E50</A>), and because it seems the logical thing to do. It would not surprise me if compiler implementers <NOBR>disagree.)<SCRIPT>create_link(103);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp104"></A><A NAME="AUTO00094"></A>Exactly the same problem occurs here as it does in a constructor. The exception from the destructor of <CODE>T2</CODE> will terminate the body of the destructor without deleting the resource held by <CODE>p1_</CODE>. The solutions are likewise similar to those applied to a constructor.For exa
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -