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

📄 mc3.htm

📁 高效c++编程
💻 HTM
📖 第 1 页 / 共 5 页
字号:
</PRE>
</UL><A NAME="66918"></A><p><A NAME="dingp66"></A>
but this is probably no safer than our original code. If one of the calls to <CODE>operator&lt;&lt;</CODE> in the <CODE>catch</CODE> block results in an exception being thrown, we're back where we started, with an exception leaving the <CODE>Session</CODE> <NOBR>destructor.<SCRIPT>create_link(66);</SCRIPT>
</NOBR></P><A NAME="66923"></A>
<P><A NAME="dingp67"></A>
We could always put a <CODE>try</CODE> block inside the <CODE>catch</CODE> block, but that seems a bit extreme. Instead, we'll just forget about logging <CODE>Session</CODE> destructions if <CODE>logDestruction</CODE> throws an <NOBR>exception:<SCRIPT>create_link(67);</SCRIPT>
</NOBR></p>
<A NAME="66929"></A>
<UL><PRE>Session::~Session()
{
  try {
    logDestruction(this);
  }
  catch (...) {  }
}
</PRE>
</UL><A NAME="66927"></A>
<P><A NAME="dingp68"></A>
The <CODE>catch</CODE> block appears to do nothing, but appearances can be deceiving. That block prevents exceptions thrown from <CODE>logDestruction</CODE> from propagating beyond <CODE>Session</CODE>'s destructor. That's all it needs to do. We can now rest easy knowing that if a <CODE>Session</CODE> object is destroyed as part of stack unwinding, <CODE>terminate</CODE> will not be <NOBR>called.<SCRIPT>create_link(68);</SCRIPT>
</NOBR></P><A NAME="50936"></A>
<P><A NAME="dingp69"></A>
There is a second reason why it's bad practice to allow exceptions to propagate out of destructors. If an exception is thrown from a destructor and is not caught there, that destructor won't run to completion. (It will stop at the point where the exception is thrown.) If the destructor doesn't run to completion, it won't do everything it's supposed to do. For example, consider a modified version of the <CODE>Session</CODE> class where the creation of a session starts a database transaction and the termination of a session ends that <NOBR>transaction:<SCRIPT>create_link(69);</SCRIPT>
</NOBR></p>
<A NAME="66973"></A>
<UL><PRE><A NAME="p61"></A>
Session::Session()          // to keep things simple,
{                           // this ctor handles no
                            // exceptions
  logCreation(this);
  startTransaction();       // start DB transaction
}
<A NAME="50888"></A>
Session::~Session()
{
  logDestruction(this);
  endTransaction();         // end DB transaction
}
</PRE>
</UL><A NAME="50881"></A>

<P><A NAME="dingp70"></A>
Here, if <CODE>logDestruction</CODE> throws an exception, the transaction started in the <CODE>Session</CODE> constructor will never be ended. In this case, we might be able to reorder the function calls in <CODE>Session</CODE>'s destructor to eliminate the problem, but if <CODE>endTransaction</CODE> might throw an exception, we've no choice but to revert to <CODE>try</CODE> and <CODE>catch</CODE> <NOBR>blocks.<SCRIPT>create_link(70);</SCRIPT>
</NOBR></P><A NAME="50973"></A>
<P><A NAME="dingp71"></A>
We thus find ourselves with two good reasons for keeping exceptions from propagating out of destructors. First, it prevents <CODE>terminate</CODE> from being called during the stack-unwinding part of exception propagation. Second, it helps ensure that destructors always accomplish everything they are supposed to accomplish. Each argument is convincing in its own right, but together, the case is ironclad. (If you're <I>still</I> not convinced, turn to <A HREF="../MAGAZINE/SU_FRAME.HTM" TARGET="_top" onMouseOver = "self.status = 'Link to Herb Sutters article'; return true" onMouseOut = "self.status = self.defaultStatus">Herb Sutter's article</A>; in particular, to the section entitled, <A HREF="../MAGAZINE/SU_FRAME.HTM#destruct" TARGET="_top" onMouseOver = "self.status = 'Link to Destructors That Throw and Why They are Evil'; return true" onMouseOut = "self.status = self.defaultStatus">"Destructors That Throw and Why They're Evil</A>.)<SCRIPT>create_link(71);</SCRIPT>
</P>

<A NAME="76790"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC3.HTM#39749">Item 11: Prevent exceptions from leaving destructors</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MC3.HTM#38224">Item 13: Catch exceptions by reference</A></FONT></DIV>
<P><A NAME="dingp72"></A><font ID="mititle">Item 12: &nbsp;Understand how throwing an exception differs from passing a parameter or calling a virtual function.</font><SCRIPT>create_link(72);</SCRIPT>
</P>

<A NAME="76791"></A>
<P><A NAME="dingp73"></A><A NAME="76792"></A>
The syntax for declaring function parameters is almost the same as that for <CODE>catch</CODE> <NOBR>clauses:<SCRIPT>create_link(73);</SCRIPT>
</NOBR></P><A NAME="67026"></A>
<UL><PRE>
class Widget { ... };                 // some class; it makes no
                                      // difference what it is
</PRE>
</UL><A NAME="67063"></A>
<UL><PRE>
void f1(Widget w);                    // all these functions
void f2(Widget&amp; w);                   // take parameters of
void f3(const Widget&amp; w);             // type Widget, Widget&amp;, or
void f4(Widget *pw);                  // Widget*
void f5(const Widget *pw);
</PRE>
</UL><A NAME="67076"></A>
<UL><PRE>
catch (Widget w) ...                  // all these catch clauses
catch (Widget&amp; w)   ...               // catch exceptions of
catch (const Widget&amp; w) ...           // type Widget, Widget&amp;, or
catch (Widget *pw) ...                // Widget*
catch (const Widget *pw) ...
</PRE>
</UL><P><A NAME="dingp74"></A><A NAME="67015"></A>
You might therefore assume that passing an exception from a <CODE>throw</CODE> site to a <CODE>catch</CODE> clause is basically the same as passing an argument <A NAME="p62"></A>from a function call site to the function's parameter. There are some similarities, to be sure, but there are significant differences, <NOBR>too.<SCRIPT>create_link(74);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp75"></A><A NAME="67038"></A>
Let us begin with a similarity. You can pass both function parameters and exceptions by value, by reference, or by pointer. What <I>happens</I> when you pass parameters and exceptions, however, is quite different. This difference grows out of the fact that when you call a function, control eventually returns to the call site (unless the function fails to return), but when you throw an exception, control does <I>not</I> return to the <CODE>throw</CODE> <NOBR>site.<SCRIPT>create_link(75);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp76"></A><A NAME="68165"></A>
Consider a function that both passes a <CODE>Widget</CODE> as a parameter and throws a <CODE>Widget</CODE> as an <NOBR>exception:<SCRIPT>create_link(76);</SCRIPT>
</NOBR></P><A NAME="67103"></A>
<UL><PRE>// function to read the value of a Widget from a stream
istream operator&gt;&gt;(istream&amp; s, Widget&amp; w);
</PRE>
</UL><A NAME="67104"></A>
<UL><PRE>void passAndThrowWidget()
{
  Widget localWidget;
</PRE>
</UL><A NAME="67122"></A>
<UL><PRE>  cin &gt;&gt; localWidget;          // pass localWidget to operator&gt;&gt;
</PRE>
</UL><A NAME="67123"></A>
<UL><PRE>  throw localWidget;           // throw localWidget as an exception
}
</PRE>
</UL><P><A NAME="dingp77"></A><A NAME="67124"></A>
When <CODE>localWidget</CODE> is passed to <CODE>operator&gt;&gt;</CODE>, no copying is performed. Instead, the reference <CODE>w</CODE> inside <CODE>operator&gt;&gt;</CODE> is bound to <CODE>localWidget</CODE>, and anything done to <CODE>w</CODE> is really done to <CODE>localWidget</CODE>. It's a different story when <CODE>localWidget</CODE> is thrown as an exception. Regardless of whether the exception is caught by value or by reference (it can't be caught by pointer &#151; that would be a type mismatch), a copy of <CODE>localWidget</CODE> will be made, and it is the <I>copy</I> that is passed to the <CODE>catch</CODE> clause. This must be the case, because <CODE>localWidget</CODE> will go out of scope once control leaves <CODE>passAndThrowWidget</CODE>, and when <CODE>localWidget</CODE> goes out of scope, its destructor will be called. If <CODE>localWidget</CODE> itself were passed to a <CODE>catch</CODE> clause, the clause would receive a destructed <CODE>Widget</CODE>, an ex-<CODE>Widget</CODE>, a former <CODE>Widget</CODE>, the carcass of what once was but is no longer a <CODE>Widget</CODE>. That would not be useful, and that's why C++ specifies that an object thrown as an exception is <I>always</I> <NOBR>copied.<SCRIPT>create_link(77);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp78"></A><A NAME="67260"></A>
This copying occurs even if the object being thrown is not in danger of being destroyed. For example, if <CODE>passAndThrowWidget</CODE> declares <CODE>localWidget</CODE> to be <NOBR>static,<SCRIPT>create_link(78);</SCRIPT>
</NOBR></P><A NAME="67266"></A>
<UL><PRE>void passAndThrowWidget()
{
  static Widget localWidget;        // this is now static; it
                                    // will exist until the
                                    // end of the program
</PRE>
</UL><A NAME="67267"></A>
<UL><PRE><A NAME="p63"></A>
  cin &gt;&gt; localWidget;               // this works as before
</PRE>
</UL><A NAME="67268"></A>
<UL><PRE>
  throw localWidget;                // a copy of localWidget is
}                                   // still made and thrown
</PRE>
</UL><P><A NAME="dingp79"></A><A NAME="67264"></A>
a copy of <CODE>localWidget</CODE> would still be made when the exception was thrown. This means that even if the exception is caught by reference, it is not possible for the <CODE>catch</CODE> block to modify <CODE>localWidget</CODE>; it can only modify a <I>copy</I> of <CODE>localWidget</CODE>. This mandatory copying of exception objects helps explain another difference between parameter passing and throwing an exception: the latter is typically much slower than the former (see <A HREF="#40989" onMouseOver = "self.status = 'Link to MEC++ Item 15'; return true" onMouseOut = "self.status = self.defaultStatus">Item 15</A>).<SCRIPT>create_link(79);</SCRIPT>
</P>    <P><A NAME="dingp80"></A><A NAME="79406"></A>
When an object is copied for use as an exception, the copying is performed by the object's copy constructor. This copy constructor is the one in the class corresponding to the object's <A HREF="./MCINTRFR.HTM#72671" TARGET="_top" onMouseOver = "self.status = 'Link to static type'; return true" onMouseOut = "self.status = self.defaultStatus"><I>static</I> type</a>, not its <A HREF="./MCINTRFR.HTM#72671" TARGET="_top" onMouseOver = "self.status = 'Link to dynamic type'; return true" onMouseOut = "self.status = self.defaultStatus">dynamic type</a>. For example, consider this slightly modified version of <CODE>passAndThrowWidget</CODE>:<SCRIPT>create_link(80);</SCRIPT>
</P><A NAME="79428"></A>
<UL><PRE>class Widget { ... };
class SpecialWidget: public Widget { ... };
</PRE>
</UL><A NAME="79433"></A>
<UL><PRE>void passAndThrowWidget()
{
  SpecialWidget localSpecialWidget;
</PRE>
</UL><A NAME="79430"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="79437"></A>
<UL><PRE>
  Widget&amp; rw = localSpecialWidget;      // rw refers to a
                                        // SpecialWidget
</PRE>
</UL><A NAME="79436"></A>
<UL><PRE>
  throw rw;                             // this throws an
                                        // exception of type
}                                       // Widget!
</PRE>
</UL><P><A NAME="dingp81"></A><A NAME="79444"></A>
Here a <CODE>Widget</CODE> exception is thrown, even though <CODE>rw</CODE> refers to a <CODE>SpecialWidget</CODE>. That's because <CODE>rw</CODE>'s static type is <CODE>Widget</CODE>, not <CODE>SpecialWidget</CODE>. That <CODE>rw</CODE> actually refers to a <CODE>SpecialWidget</CODE> is of no concern to your compilers; all they care about is <CODE>rw</CODE>'s static type. This behavior may not be what you want, but it's consistent with all other cases in which C++ copies objects. Copying is always based on an object's static type (but see <A HREF="./MC5_FR.HTM#5341" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 25'; return true" onMouseOut = "self.status = self.defaultStatus">Item 25</A> for a technique that lets you make copies on the basis of an object's dynamic <NOBR>type).<SCRIPT>create_link(81);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp82"></A><A NAME="79469"></A>
The fact that exceptions are copies of other objects has an impact on how you propagate exceptions from a <CODE>catch</CODE> block. Consider these two <CODE>catch</CODE> blocks, which at first glance appear to do the same <NOBR>thing:<SCRIPT>create_link(82);</SCRIPT>
</NOBR></P><A NAME="79470"></A>
<UL><PRE><A NAME="p64"></A>
catch (Widget&amp; w)                 // catch Widget exceptions
{
  ...                             // handle the exception
</PRE>
</UL><A NAME="79471"></A>
<UL><PRE>
  throw;                          // rethrow the exception so it
}                                 // continues to propagate
</PRE>
</UL><A NAME="79479"></A>
<UL><PRE>
catch (Widget&amp; w)                 // catch Widget exceptions
{
  ...                             // handle the exception
</PRE>
</UL><A NAME="79480"></A>
<UL><PRE>
  throw w;                        // propagate a copy of the
}                                 // caught exception
</PRE>
</UL><P><A NAME="dingp83"></A><A NAME="79477"></A>
The only difference between these blocks is that the first one rethrows the current exception, while the second one throws a new copy of the current exception. Setting aside the performance cost of the additional copy operation, is there a difference between these <NOBR>approaches?<SCRIPT>create_link(83);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp84"></A><A NAME="11583"></A>
There is. The first block rethrows the <I>current</I> exception, regardless of its type. In particular, if the exception originally thrown was of type <CODE>SpecialWidget</CODE>, the first block would propagate a <CODE>SpecialWidget</CODE> exception, even though <CODE>w</CODE>'s static type is <CODE>Widget</CODE>. This is because no copy is made when the exception is rethrown. The second <CODE>catch</CODE> block throws a <I>new</I> exception, which will always be of type <CODE>Widget</CODE>, because that's <CODE>w</CODE>'s static type. In general, you'll want to use <NOBR>the<SCRIPT>create_link(84);</SCRIPT>
</NOBR></P><A NAME="79544"></A>
<UL><PRE>throw;
</PRE>
</UL><P><A NAME="dingp85"></A><A NAME="79541"></A>
syntax to rethrow the current exception, because there's no chance that that will change the type of the exception being propagated. Furthermore, it's more efficient, because there's no need to generate a new exception <NOBR>object.<SCRIPT>create_link(85);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp86"></A><A NAME="67318"></

⌨️ 快捷键说明

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