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

📄 mi12.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 12: Understand how throwing an exception differs from passing a parameter or calling a virtual function</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 2; setCurrentMax(2);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "MI12_DIR.HTM";
var dingtext = "Item M12, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName = "M12: Exceptions vs. parameters and virtual funcs" -->
<A NAME="76790"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI11_FR.HTM" TARGET="_top">Item 11: Prevent exceptions from leaving destructors</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI13_FR.HTM" TARGET="_top">Item 13: Catch exceptions by reference</A></FONT></DIV>


<P><A NAME="dingp1"></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(1);</SCRIPT>
</P>

<A NAME="76791"></A>
<P><A NAME="dingp2"></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(2);</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="dingp3"></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(3);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp4"></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(4);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp5"></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(5);</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><A NAME="67124"></A>
<P><A NAME="dingp6"></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(6);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp7"></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(7);</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="dingp8"></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="./MI15_FR.HTM#40989" TARGET="_top">Item&nbsp;15</A>).<SCRIPT>create_link(8);</SCRIPT>
</P>    <P><A NAME="dingp9"></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="./MIINTRFR.HTM#72671" TARGET="_top"><I>static</I> type</a>, not its <A HREF="./MIINTRFR.HTM#72671" TARGET="_top">dynamic type</a>. For example, consider this slightly modified version of <CODE>passAndThrowWidget</CODE>:<SCRIPT>create_link(9);</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="dingp10"></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="./MI25_FR.HTM#5341" TARGET="_top">Item 25</A> for a technique that lets you make copies on the basis of an object's dynamic <NOBR>type).<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp11"></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(11);</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="dingp12"></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(12);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp13"></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(13);</SCRIPT>
</NOBR></P><A NAME="79544"></A>
<UL><PRE>throw;
</PRE>
</UL><P><A NAME="dingp14"></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(14);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp15"></A><A NAME="67318"></A>
(Incidentally, the copy made for an exception is a <I>temporary</I> object. As <A HREF="./MI19_FR.HTM#41177" TARGET="_top">Item 19</A> explains, this gives compilers the right to optimize it out of existence. I wouldn't expect your compilers to work that hard, however. Exceptions are supposed to be rare, so it makes little sense for compiler vendors to pour a lot of energy into their <NOBR>optimization.)<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>    <P><A NAME="dingp16"></A><A NAME="67201"></A>
Let us examine the three kinds of <CODE>catch</CODE> clauses that could catch the <CODE>Widget</CODE> exception thrown by <CODE>passAndThrowWidget</CODE>. They <NOBR>are:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P><A NAME="67202"></A>
<UL><PRE>
catch (Widget w) ...                // catch exception by value

⌨️ 快捷键说明

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