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

📄 chap01.htm

📁 This is the second part of that lab manual to teach you how to make real-time programme and how to d
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<B>g(&#160;)</B> so it throws exceptions and the new <B>g(&#160;)</B> is linked
in with <B>f(&#160;)</B>. Now <B>f(&#160;)</B> begins to throw a new exception,
unbeknown to the creator of <B>f(&#160;)</B>. Thus the exception specification
is violated.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>my_unexpected(&#160;)</B> function
has no arguments or return value, following the proper form for a custom
<B>unexpected(&#160;)</B> function. It simply prints a message so you can see it
has been called, then exits the program (<B>exit(0)</B> is used here so that the
book&#8217;s <B>make</B> process is not aborted). Your new
<B>unexpected(&#160;)</B> function must not return (that is, you can write the
code that way but it&#8217;s an error). However, it can throw another exception
(you can even rethrow the same exception), or call <B>exit(&#160;)</B> or
<B>abort(&#160;)</B>. If <B>unexpected(&#160;)</B> throws an exception, the
search for the handler starts at the function call that threw the unexpected
exception. (This behavior is unique to
<B>unexpected(&#160;)</B>.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although the <B>new_handler(&#160;)</B>
function pointer can be null and the system will do something sensible, the
<B>unexpected(&#160;)</B> function pointer should never be null. The default
value is <B>terminate(&#160;)</B> (mentioned later), but whenever you use
exceptions and specifications you should write your own
<B>unexpected(&#160;)</B> to log the error and either rethrow it, throw
something new, or terminate the program.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, the <B>try</B>
block is within a <B>for</B> loop so all the possibilities are exercised. Note
that this is a way to achieve something like resumption
<A NAME="Index42"></A>&#8211; nest the <B>try</B> block inside a <B>for</B>,
<B>while</B>, <B>do</B>, or <B>if</B> and cause any exceptions to attempt to
repair the problem; then attempt the <B>try</B> block again.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Only the <B>Up</B> and <B>Fit</B>
exceptions are caught because those are the only ones the programmer of
<B>f(&#160;)</B> said would be thrown. Version two of <B>g(&#160;)</B> causes
<B>my_unexpected(&#160;)</B> to be called because <B>f(&#160;)</B> then throws
an <B>int</B>. (You can throw any type, including a built-in
type.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the call to
<B>set_unexpected(&#160;)</B>, the return value is ignored, but it can also be
saved in a pointer to function and restored
later.</FONT><A NAME="_Toc312374117"></A><A NAME="_Toc519041886"></A><BR></P></DIV>
<A NAME="Heading28"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Better exception specifications?</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You may feel the existing exception
specification rules aren&#8217;t very safe, and that</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font> f();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><I>should</I> mean that no exceptions are
thrown from this function. If the programmer wants to throw any type of
exception, you may think he or she <I>should </I>have to say</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font> f() <font color=#0000ff>throw</font>(...); <font color=#009900>// Not in C++</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This would surely be an improvement
because function declarations would be more explicit. Unfortunately you
can&#8217;t always know by looking at the code in a function whether an
exception will be thrown &#8211; it could happen because of a memory allocation,
for example. Worse, existing functions written before exception handling was
introduced may find themselves inadvertently throwing exceptions because of the
functions they call (which may be linked into new, exception-throwing versions).
Thus, the ambiguity, so</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font> f();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">means &#8220;Maybe I&#8217;ll throw an
exception, maybe I won&#8217;t.&#8221; This ambiguity is necessary to avoid
hindering code
evolution.</FONT><A NAME="_Toc312374118"></A><A NAME="_Toc519041887"></A><BR></P></DIV>
<A NAME="Heading29"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Catching any exception<BR><A NAME="Index43"></A><A NAME="Index44"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As mentioned, if your function has no
exception specification, <I>any</I> type of exception can be thrown. One
solution to this problem is to create a handler that <I>catches</I> any type of
exception. You do this using the
ellipses<A NAME="Index45"></A><A NAME="Index46"></A> in the argument list
(&aacute; la C):</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>catch</font>(...) {
  cout &lt;&lt; <font color=#004488>"an exception was thrown"</font> &lt;&lt; endl;
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This will catch any exception, so
you&#8217;ll want to put it at the <I>end</I> of your list of handlers to avoid
pre-empting any that follow it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The ellipses give you no possibility to
have an argument or to know anything about the type of the exception. It&#8217;s
a
catch-all.</FONT><A NAME="_Toc312374119"></A><A NAME="_Toc519041888"></A><BR></P></DIV>
<A NAME="Heading30"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Rethrowing an exception<BR><A NAME="Index47"></A><A NAME="Index48"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Sometimes you&#8217;ll want to rethrow
the exception that you just caught, particularly when you use the ellipses to
catch any exception because there&#8217;s no information available about the
exception. This is accomplished by saying <B>throw</B> with no
argument:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>catch</font>(...) {
  cout &lt;&lt; <font color=#004488>"an exception was thrown"</font> &lt;&lt; endl;
  <font color=#0000ff>throw</font>;
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Any further <B>catch</B> clauses for the
same <B>try</B> block are still ignored &#8211; the <B>throw</B> causes the
exception to go to the exception handlers in the next-higher context. In
addition, everything about the exception object is preserved, so the handler at
the higher context that catches the specific exception type is able to extract
all the information from that
object.</FONT><A NAME="_Toc312374120"></A><A NAME="_Toc519041889"></A><BR></P></DIV>
<A NAME="Heading31"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Uncaught
exceptions<BR><A NAME="Index49"></A><A NAME="Index50"></A><A NAME="Index51"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If none of the exception handlers
following a particular <B>try</B> block matches an exception, that exception
moves to the next-higher context, that is, the function or <B>try</B> block
surrounding the <B>try</B> block that failed to catch the exception. (The
location of this higher-context <B>try</B> block is not always obvious at first
glance.) This process continues until, at some level, a handler matches the
exception. At that point, the exception is considered &#8220;caught,&#8221; and
no further searching occurs.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If no handler at any level catches the
exception, it is &#8220;uncaught&#8221; or &#8220;unhandled.&#8221; An uncaught
exception also occurs if a new exception is thrown before an existing exception
reaches its handler &#8211; the most common reason for this is that the
constructor for the exception object itself causes a new
exception.</FONT><BR></P></DIV>
<A NAME="Heading32"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H4 ALIGN="LEFT">
terminate(&#160;)</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If an exception is uncaught, the special
function <B>terminate(&#160;)</B> is automatically called. Like
<B>unexpected(&#160;)</B>, terminate is actually a pointer to a function. Its
default value is the Standard C library function
<B>abort(&#160;)<A NAME="Index52"></A><A NAME="Index53"></A></B>, which
immediately exits the program with no calls to the normal termination functions
(which means that destructors for global and static objects might not be
called). On Unix systems, <B>abort(&#160;)</B> also causes a core
dump.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">No cleanups occur for an uncaught
exception; that is, no destructors are called. If you don&#8217;t wrap your code
(including, if necessary, all the code in <B>main(&#160;)</B>)<B> </B>in a try
block followed by handlers and ending with a default handler (<B>catch(...)</B>)
to catch all exceptions, then you will take your lumps. An uncaught exception
should be thought of as a programming error.</FONT><BR></P></DIV>
<A NAME="Heading33"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H4 ALIGN="LEFT">
set_terminate(&#160;)</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can install your own
<B>terminate(&#160;)</B> function using the standard
<B>set_terminate(&#160;)</B>
<A NAME="Index54"></A><A NAME="Index55"></A>function, which returns a pointer to
the <B>terminate(&#160;)</B> function you are replacing, so you can restore it
later if you want. Your custom <B>terminate(&#160;)</B> must take no arguments
and have a <B>void</B> return value. In addition, any <B>terminate(&#160;)</B>
handler you install must not return or throw an exception, but instead must call
some sort of program-termination function. If <B>terminate(&#160;)</B> is
called, it means the problem is unrecoverable.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Like <B>unexpected(&#160;)</B>, the
<B>terminate(&#160;)</B> function pointer should never be null.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s an example showing the use
of <B>set_terminate(&#160;)</B>. Here, the return value is saved and restored so
the <B>terminate(&#160;)</B> function can be used to help isolate the section of
code where the uncaught exception is occurring:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C01:Terminator.cpp</font>
<font color=#009900>// Use of set_terminate()</font>
<font color=#009900>// Also shows uncaught exceptions</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include &lt;exception&gt;
#include &lt;iostream&gt;
#include &lt;cstdlib&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>void</font> terminator() {
  cout &lt;&lt; <font color=#004488>"I'll be back!"</font> &lt;&lt; endl;
  exit(0);
}

<font color=#0000ff>void</font> (*old_terminate)()
  = set_terminate(terminator);

<font color=#0000ff>class</font> Botch {
<font color=#0000ff>public</font>:
  <font color=#0000ff>class</font> Fruit {};
  <font color=#0000ff>void</font> f() {
    cout &lt;&lt; <font color=#004488>"Botch::f()"</font> &lt;&lt; endl;
    <font color=#0000ff>throw</font> Fruit();
  }
  ~Botch() { <font color=#0000ff>throw</font> 'c'; }
};

<font color=#0000ff>int</font> main() {
  <font color=#0000ff>try</font>{
    Botch b;
    b.f();
  } <font color=#0000ff>catch</font>(...) {
    cout &lt;&lt; <font color=#004488>"inside catch(...)"</font> &lt;&lt; endl;
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The definition of <B>old_terminate</B>
looks a bit confusing at first: It not only creates a pointer to a
function<A NAME="Index56"></A><A NAME="Index57"></A>, but it initializes that
pointer to the return value of <B>set_terminate(&#160;)</B>. Even though you may
be familiar with seeing a semicolon right after a pointer-to-function
definition, it&#8217;s just another kind of variable and may be initialized when
it is defined.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>Botch</B> not only throws an
exception inside <B>f(&#160;)</B>, but also in its destructor. This is one of
the situations that causes a call to <B>terminate(&#160;)</B>, as you can see in
<B>main(&#160;)</B>. Even though the exception handler says <B>catch(...)</B>,
which would seem to catch everything and leave no cause for
<B>terminate(&#160;)</B> to be called, <B>terminate(&#160;) </B>is called
anyway, because in the process of cleaning up the objects on the stack to handle
one exception, the <B>Botch</B> destructor is called, and that generates a
second exception, forcing a call to <B>terminate(&#160;)</B>. Thus, a
destructor<A NAME="Index58"></A><A NAME="Index59"></A> that throws an exception
or causes one to be thrown is a design
error.</FONT><A NAME="_Toc305593299"></A><A NAME="_Toc305628771"></A><A NAME="_Toc312374121"></A><A NAME="_Toc519041890"></A><BR></P></DIV>
<A NAME="Heading34"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Function-level try blocks</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">[ Leave this out of the compile for now
by leaving off the colon after the //]</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C01:FunctionTryBlock.cpp</font>
<font color=#009900>// Function-level try blocks</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-msc}</font>
<font color=#009900>//{-bor}</font>
#include &lt;iostream&gt;

⌨️ 快捷键说明

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