⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reeves.htm

📁 高效c++编程
💻 HTM
📖 第 1 页 / 共 5 页
字号:
          does not hide another.</a><SCRIPT>create_link(46,'#FFFFCC');</SCRIPT>


</OL>
  </P>
<A NAME="AUTO00037"></A>
  </TD></TR>
<TR>
    <TD VALIGN="top"><A NAME="dingp47"></A><B><a href="./REEVES.HTM#goal7">VII.</a></B></TD>
    <TD> <B>Never depend upon destructors for functionality in any situation where
  fault-tolerance is required.</B><SCRIPT>create_link(47,'#FFFFCC');</SCRIPT>


<A NAME="AUTO00038"></A>

    <OL TYPE="1">
      <A NAME="dingp48"></A>
        <LI VALUE="13"> <a href="./REEVES.HTM#guide13">Do not throw exceptions from a destructor 
          body.</a><SCRIPT>create_link(48,'#FFFFCC');</SCRIPT>

<A NAME="AUTO00039"></A>
      <A NAME="dingp49"></A>
        <LI VALUE="14"> <a href="./REEVES.HTM#guide14">Do not arbitrarily handle all exceptions 
          propagating from a destructor.</a><SCRIPT>create_link(49,'#FFFFCC');</SCRIPT>


</OL>

<A NAME="AUTO00040"></A>

</TD></TR>
<TR>
    <TD VALIGN="top"><A NAME="dingp50"></A><B><a href="./REEVES.HTM#goal8">VIII.</a></B></TD>
    <TD><B> Do not get too paranoid.</B><SCRIPT>create_link(50,'#FFFFCC');</SCRIPT>


</OL>
</TD></TR></TABLE>

<A NAME="AUTO00041"></A>

<P><A NAME="dingp51"></A><FONT ID="aititle">Catching and Propagating Exceptions</FONT><SCRIPT>create_link(51);</SCRIPT>
</P>

<P><A NAME="dingp52"></A><A NAME="AUTO00042"></A>We must state some assumptions before we actually start to deal with exceptions.  We will assume that an exception thrown by a lower level routine indicates that the routine failed.  In a follow-on article [<A HREF="#ref3" ONMOUSEOVER = "self.status = 'Reference 3'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Reference 3</A>], I present some goals and guidelines for throwing exceptions.  While there is no way to ensure that exceptions are only used to indicate a failure condition, (and there are a couple of situations where they may not), it is probably safe to assume that most programmers are not going to accept the overhead inherent in an exception throw (see Items <SCRIPT>sendmetoo(12,76790,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'M12'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">M12</A> and <SCRIPT>sendmetoo(15,40989,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'M15'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">M15</A>) if other mechanisms are available to indicate normal <NOBR>completion.<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp53"></A><A NAME="AUTO00043"></A>We will also assume that exceptions are typically a rare occurrence in a well designed program.  This means, in general, that the condition causing the error is not likely to be one that is easily anticipated and handled by the next higher level routine.  The entire exception handling mechanism is designed to pass information from the point where an error is detected up the calling chain to a higher level of the program where enough information exists to be able to handle the error.  In many cases, exceptions are going to propagate all the way to a human operator.  Therefore, it is probably safe to say that the vast majority of code will simply propagate exceptions from lower level routines to higher level <NOBR>ones.<SCRIPT>create_link(53);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp54"></A><A NAME="AUTO00044"></A>It is this propagation of exceptions that causes the grief and anguish. The function that throws the exception knows what state it is in and it chooses when to throw the exception.  The function that finally handles the exception remains in control &#151; it must necessarily branch into a separate exception handling path &#151; but in handling the exception it recovers from the error and continues.  All of the intervening functions between the one that originally threw the exception and the one that finally handles it are a problem.  When an exception propagates out of a function, it indicates that the function failed.  It is as if the function threw the exception, but without any control over where the throw occurs.  The propagating exception terminates the function, unwinds its stack, and continues on its <NOBR>way.<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp55"></A><A NAME="AUTO00045"></A>If not anticipated and handled correctly, this propagation of an exception can leave dangling resources, bad or even undefined states, and in general cause more problems than the original error.  Propagating exceptions destroy otherwise perfectly rational flow of control through a program.  In a very real sense, exceptions are &quot;goto's from hell&quot;.  So how do we cope with <NOBR>them?<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp56"></A><A NAME="AUTO00046"></A><A NAME="goal1"></A><B>Goal I.</B>  <B>When you propagate an exception, try to leave the object in the state it had when the function was entered</B>.  This is the Golden Rule of exception handling.  It is not going to do much good for a program to handle an exception if the program has gone into an invalid state as a result of the exception propagating to the point where it could be handled. Writing code that meets this goal is very difficult.  In truth, it may often be impossible.  Nevertheless, I put it first because it is the target that should always be the ultimate goal.  How to go about this is a complicated subject, and there are more detailed articles on dealing with it [<A HREF="#ref2" ONMOUSEOVER = "self.status = 'Reference 2'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Reference 2</A>].  I will only give a few simple guidelines <NOBR>here.<SCRIPT>create_link(56);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp57"></A><a name="AUTO00047"></a><A NAME="guide1"></A><I>Guideline 
  1. Make sure your const functions really are const.</I> The first step in making 
  sure objects stay in good states is determining which operations on the object 
  actually change the state. The C++ language assists us in this regard by requiring 
  member functions that can be applied to constant objects to be designated as 
  const functions (see 
  <SCRIPT>sendmetoo(21,6003,'E');</SCRIPT>
  ONMOUSEOVER = "self.status = 'Item E21'; return true" ONMOUSEOUT = "self.status 
  = self.defaultStatus">Item E21</A>). As we all know however, const can be subverted, 
  either by designating some members mutable (again, see 
  <SCRIPT>sendmetoo(21,6003,'E');</SCRIPT>
  ONMOUSEOVER = "self.status = 'Item E21'; return true" ONMOUSEOUT = "self.status 
  = self.defaultStatus">Item E21</A>), or with a <CODE>const_cast</CODE> (see 
  <SCRIPT>sendmetoo(2,77216,'M');</SCRIPT>
  ONMOUSEOVER = "self.status = 'Item M2'; return true" ONMOUSEOUT = "self.status 
  = self.defaultStatus">Item M2</A></NOBR>). If your <CODE>const</CODE> functions 
  really do not change the state of the object, then you can safely ignore them 
  &#151; exceptions that propagate from them will not affect <NOBR>anything.
  <SCRIPT>create_link(57);</SCRIPT>
  </NOBR></P>

<P><A NAME="dingp58"></A><A NAME="AUTO00048"></A>On the other hand, if your <CODE>const</CODE> functions do change the state of the object, then you must treat your const functions like your non-<CODE>const</CODE> functions &#151; only more so.  As a user, I would be very annoyed if I discovered that a <CODE>const</CODE> function could actually change the object into a bad state under some circumstances.  If you discover such a function in your class, I would urge reconsidering whether it should be a <CODE>const</CODE> function.  An example of this actually occurs in the <SCRIPT>sendmetoo(49,8392,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Standard C++ Library'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Standard C++ Library</A>.  The <CODE>basic_string</CODE> function <CODE>c_str()</CODE> is a const function, but the specification is clear that an implementation can reallocate the internal character array if necessary to make room for the terminating <CODE>NULL</CODE> character.  This could throw a <CODE>bad_alloc</CODE> exception.  When a <CODE>const</CODE> function exhibits such behavior, it has numerous ramifications, most of them <NOBR>bad.<SCRIPT>create_link(58);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp59"></A><A NAME="AUTO00049"></A><A NAME="guide2"></A><I>Guideline 2. Perform exception prone operations early, and/or through temporaries.</I>  Consider the <CODE>Stack::push</CODE> <NOBR>function:<SCRIPT>create_link(59);</SCRIPT>
</NOBR></P>

<A NAME="AUTO00149"></A><UL><PRE>
template&lt;class T&gt;
Stack&lt;T&gt;::push(T element)
{
  if (top_ == nelems_) {
    T* new_buffer = new T[nelems_*= 2];    //&gt;x1
    for (int i = 0; i &lt; top_; i++)
      new_buffer[i] = v_[i];               //&gt;x2
    delete[ ]v_;                           //&gt;x3
    v_ = new_buffer;
  }
  v_[top_++] = element;                    //&gt;x4
}
</PRE>
</UL>

<P><A NAME="dingp60"></A><A NAME="AUTO00050"></A>Each of the comments indicates a statement that potentially can throw an exception.  For example, the statement at <CODE>x1</CODE> can throw a <CODE>bad_alloc</CODE> exception if there is not enough memory to satisfy the request.  In this statement, the member variable <CODE>nelems_</CODE> is increased, and then used as the size of the new request (a typical C idiom).  If this request fails and throws an exception, it propagates out of <CODE>push()</CODE> leaving <CODE>nelems_</CODE> set to the new value.  The <CODE>Stack</CODE> object is now in an bad <NOBR>state.<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp61"></A><A NAME="AUTO00051"></A>Besides doing memory allocation, statement <CODE>x1</CODE> also uses <CODE>T::T()</CODE>.  Similarly, statements <CODE>x2</CODE> and <CODE>x4</CODE> use <CODE>T::operator=()</CODE>, and <CODE>x3</CODE> uses <CODE>T::~T()</CODE>.  Each of these functions might throw an exception.  For now I will ignore the possibility of exceptions from destructors, but <CODE>x1</CODE>, <CODE>x2</CODE>, and <CODE>x4</CODE> are all potential problems.  If we propagate an exception from one of these operations it will indicate that the <CODE>push()</CODE> operation failed, but in what state will we leave the <CODE>Stack</CODE> <NOBR>object?<SCRIPT>create_link(61);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp62"></A><A NAME="AUTO00052"></A>An exception caused by a failure in <CODE>T::T()</CODE> at <CODE>x1</CODE> has the same effect as a <CODE>bad_alloc</CODE> exception.  If we use a temporary variable for the new value of <CODE>nelems_</CODE>, then an exception from <CODE>x1</CODE> can safely be allowed to propagate. We will assume that if <CODE>T::T()</CODE> throws an exception the runtime mechanism will destroy all the already constructed elements in the array and release the allocated memory. (Except in pathological cases, which are discussed below, the standard guarantees this <NOBR>behavior.)<SCRIPT>create_link(62);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp63"></A><A NAME="AUTO00053"></A>If one of the assignments in the loop at <CODE>x2</CODE> fails, the internal state of the <CODE>Stack</CODE> object is still good, but we have a memory leak since the buffer pointed to by <CODE>new_buffer</CODE> will never be deleted.  We will discuss resource leaks in more detail under <A HREF="./REEVES.HTM#goal4" ONMOUSEOVER = "self.status = 'Goal IV'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Goal IV</A> <NOBR>below.<SCRIPT>create_link(63);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp64"></A><A NAME="AUTO00054"></A>If the assignment at <CODE>x4</CODE> fails, variable <CODE>top_</CODE> will already have been incremented.  In this case, the exception will indicate that the <CODE>push()</CODE> failed, but the internal state of the object will have been updated as though it succeeded.  Again, we are in a bad <NOBR>state.<SCRIPT>create_link(64);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp65"></A><A NAME="AUTO00055"></A>In order to make sure that possible exceptions that propagate out of <CODE>push()</CODE> leave the object in its original state (and do not cause a memory leak), a number of modifications are <NOBR>necessary:<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P>

<A NAME="AUTO00056"></A>

<OL>
<A NAME="dingp66"></A><LI> Use a temporary for the new array size.<SCRIPT>create_link(66);</SCRIPT>


<A NAME="AUTO00057"></A>

<A NAME="dingp67"></A><LI> Use a temporary object of the template class <CODE>auto_array_ptr</CODE> (see below) to create an automatic variable that manages the new buffer.<SCRIPT>create_link(67);</SCRIPT>


<A NAME="AUTO00058"></A>

<A NAME="dingp68"></A><LI> After the new buffer is initialized, swap ownership of the new buffer between the <CODE>auto_array_ptr object</CODE> and the stack.<SCRIPT>create_link(68);</SCRIPT>


<A NAME="AUTO00059"></A>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -