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

📄 ch06.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<h3> <a name="Heading7">The Challenges of Implementation of Exception Handling</a></h3><p>The difficulties in implementing exception handling arise from several factors.   First, the implementation must ensure that the proper handler for a specific   exception is found. </p><p>Secondly, exception objects can be polymorphic; in that case, the implementation   also considers handlers of base classes when it cannot locate a matching handler   for a derived object.. This requirement implies a sort of runtime type checking   to recover the dynamic type of the exception object. Yet C++ did not have any   runtime type checking facilities whatsoever before exception handling was developed;   these facilities had to be created from scratch for that purpose. </p><p>As an additional complication, the implementation must invoke the destructors   of all local objects that were constructed on the path from a <tt>try</tt> block   to a <tt>throw</tt> expression before control passes to the appropriate handler.   This process is called <i>stack</i> <i>unwinding</i> (the stack unwinding process   is discussed in further detail later in this chapter). Because early C++ compilers   translated the C++ source file into pure C and only then compiled the code into   machine code, the implementers of exception handling had to implement runtime   type identification and stack unwinding in C. Fortunately, these obstacles have   all been overcome.</p><h2> <a name="Heading8">Applying Exception Handling</a></h2><p>Exception handling is a flexible and sophisticated tool. It overcomes the drawbacks   of C's traditional error handling methods and it can be used to handle a variety   of runtime errors. Still, exception handling, like other language features,   can easily be misused. To use this feature effectively, it is important to understand   how the underlying runtime machinery works and what the associated performance   penalties are. The following sections delve into exception handling internals   and demonstrate how to use this tool to create robust, bulletproof applications.</p><blockquote>  <hr>  <strong>CAUTION: </strong> Some of the code samples in the following sections   use new exception handling features such as function try blocks and exception   specifications. Several compilers do not support these features yet; therefore,   it is recommended that you read the technical documentation of your compiler   to check whether it fully supports exception handling.   <hr></blockquote><h3> <a name="Heading9">Exception Handling Constituents</a></h3><p>Exception handling is a mechanism for transferring control from a point in   a program where an exception occurs to a matching handler. Exceptions are variables   of built-in data types or class objects. The exception handling mechanism consists   of four components: a <tt>try</tt><i> block</i>, a sequence of one or more <i>handlers</i>   associated with a <tt>try</tt> block, a <tt>throw</tt><i> expression</i>, and   the exception itself. The <tt>try</tt> block contains code that might throw   an exception. For example</p><pre><tt>try </tt><tt>{</tt><tt> int * p = new int[1000000]; //may throw std::bad_alloc</tt><tt>}</tt></pre><p>A <tt>try</tt> block is followed by a sequence of one or more <tt>catch</tt>   statements, or handlers, each of which handles a different type of exception.   For example</p><pre><tt>try</tt><tt>{</tt><tt> int * p = new int[1000000]; //may throw std::bad_alloc</tt><tt> //...</tt><tt>}</tt><tt>catch(std::bad_alloc&amp; ) </tt><tt>{</tt><tt>}</tt><tt>catch (std::bad_cast&amp;)</tt><tt>{</tt><tt>}</tt></pre><p>A handler is invoked only by a <tt>throw</tt> expression that is executed in   the handler's <tt>try</tt> block or in functions that are called from the handler's   <tt>try</tt> block. A <tt>throw</tt> expression consists of the keyword <tt>throw</tt>   and an <cite>assignment expression</cite>. For example</p><pre><tt>try</tt><tt>{</tt><tt> throw 5; // 5 is assigned to n in the following catch statement</tt><tt>}</tt><tt>catch(int n) </tt><tt>{</tt><tt>}</tt></pre><p>A <tt>throw</tt><i> </i>expression is similar to a <tt>return</tt> statement.   An <i>empty throw</i> is a <tt>throw</tt> statement without an operand. For   example</p><pre><tt>throw; </tt></pre><p>An empty throw inside a handler indicates a <i>rethrow</i>, which is discussed   momentarily. Otherwise, if no exception is presently being handled, executing   an empty throw calls <tt>terminate()</tt>. </p><h3> <a name="Heading10">Stack Unwinding</a></h3><p>When an exception is thrown, the runtime mechanism first searches for an appropriate   handler in the current scope. If such a handler does not exist, </p><p>the current scope is exited and the block that is higher in the calling chain   is entered into scope. This process is iterative: It continues until an appropriate   handler has been found. An exception is considered to be handled upon its entry   to a handler. At this point, the stack has been unwound and all the local objects   that were constructed on the path from a <tt>try</tt> block to a <tt>throw</tt>   expression have been destroyed. In the absence of an appropriate handler, the   program terminates. Note, however, that C++ ensures proper destruction of local   objects only when the thrown exception is handled. Whether an uncaught exception   causes the destruction of local objects during stack unwinding is implementation-dependent.   To ensure that destructors of local objects are invoked in the case of an uncaught   exception, you can add a <tt>catch</tt> <tt>all</tt> statement in <tt>main()</tt>.   For example</p><pre><tt>int main()</tt><tt>{</tt><tt>  try</tt><tt>  {</tt><tt>    //...</tt><tt>  }</tt><tt>  catch(std::exception&amp; stdexc)   // handle expected exceptions</tt><tt>  {</tt><tt>    //...</tt><tt>  }</tt><tt>  catch(...)   // ensure proper cleanup in the case of an uncaught exception</tt><tt>  {</tt><tt>  }</tt><tt>  return 0;</tt><tt>}</tt></pre><p>The stack unwinding process is very similar to a sequence of <tt>return</tt>   statements, each returning the same object to its caller. </p><h3> <a name="Heading11">Passing Exception Objects to a Handler</a></h3><p>An exception can be passed by value or by reference to its handler. The memory   for the exception that is being thrown is allocated in an unspecified way (but   it is not allocated on the free store). Some implementations use a dedicated   exception stack, on which exception objects are created. When an exception is   passed by reference, the handler receives a reference to the exception object   that is constructed on the exception stack. Passing an exception by reference   ensures its polymorphic behavior. Exceptions that are passed by value are constructed   on the stack frame of the caller. For example </p><pre><tt>#include  &lt;cstdio&gt;</tt><tt>class ExBase {/*...*/};</tt><tt>class FileEx: public ExBase {/*...*/};</tt><tt>void Write(FILE *pf)</tt><tt>{</tt><tt>  if (pf == NULL) throw FileEx();</tt><tt>  //... process pf normally</tt><tt>}</tt><tt>int main ()</tt><tt>{</tt><tt>  try</tt><tt>  {</tt><tt>    Write(NULL); //will cause a FileEx exception to be thrown</tt><tt>  }</tt><tt>  catch(ExBase&amp; exception) //catch ExBase or any object derived from it</tt><tt>  {</tt><tt>  //diagnostics and remedies   }</tt><tt>}</tt></pre><p>Repeatedly copying objects that are passed by value is costly because the exception   object can be constructed and destroyed several times before a matching handler   has been found. However, it occurs only when an exception is thrown, which only   happens in abnormal and -- hopefully -- rare situations. Under these circumstances,   performance considerations are secondary (exception handling performance is   discussed at the end of this chapter) to maintaining an application's integrity.</p><h3> <a name="Heading12">Exception Type Match</a></h3><p>The type of an exception determines which handler can catch it. The matching   rules for exceptions are more restrictive than are the matching rules for function   overloading. Consider the following example:</p><pre><tt>try</tt><tt>{</tt><tt>  throw int();</tt><tt>}</tt><tt>catch (unsigned int) //will not catch the exception from the previous try-block</tt><tt>{		 </tt><tt>}</tt></pre><p>The thrown exception is of type <tt>int</tt>, whereas the handler expects an   <tt>unsigned int</tt>. The exception handling mechanism does not consider these   to be matching types; as a result, the thrown exception is not caught. The matching   rules for exceptions allow only a limited set of conversions: For an exception   <tt>E</tt> and a handler taking <tt>T</tt> or <tt>T&amp;</tt>, the match is   valid under one of the following conditions: </p><ul>  <li>    <p> <tt>T</tt> and <tt>E</tt> are of the same type (<tt>const</tt> and <tt>volatile</tt>       specifiers are ignored)</p>  </li>  <p></p>  <li>    <p> <tt>T</tt> is an unambiguous public base class of . </p>  </li></ul><p></p><p>If <tt>E</tt> and <tt>T</tt> are pointers, the match is valid if <tt>E</tt>   and <tt>T</tt> are of the same type or if <tt>E</tt> points to an object that   is publicly and unambiguously derived from the class that is pointed to by <tt>T</tt>.   In addition, a handler of type <tt>array of T</tt> or <tt>function returning   T</tt> is transformed into <tt>pointer to T</tt> or <tt>pointer to function   returning T</tt>, respectively.</p><h3> <a name="Heading13">Exceptions as Objects</a></h3><p></p><p>As you have probably noticed, the traditional convention of returning an integer   as an error flag is problematic and unsatisfactory in OOP. The C++ exception   handling mechanism offers more flexibility, safety, and robustness. An exception   can be a fundamental type such as <tt>int</tt> or a <tt>char *</tt>. It can   be a full-fledged object as well, with data members and member functions. Such   an object can provide the exception handler with more options for recovery.   A clever exception object, for example, can have a member function that returns   a detailed verbal description of the error, instead of letting the handler to   look it up in a table or a file. It can have member functions that enable the   program to recover from the runtime error after the error has been handled properly.   Consider a logger class that appends new records to an existing log file: If   it fails to open the log file, it throws an exception. When it is caught by   the matching handler, the exception object can have a member function, which   creates a dialog box. The operator can choose recovery measures from the dialog   box, including creation of a new log file, redirecting the logger to an alternative   file, or simply allowing the system to run without a logger.</p><h3> <a name="Heading14">Exception Specification</a></h3><p>A function that might throw an exception can warn its users by specifying a   list of the exceptions that it can throw. Exception specifications are particularly   useful when users of a function can view its prototype but cannot access its   source file. Following is an example of specifying an exception:</p><pre><tt>class Zerodivide{/*..*/};</tt><tt>int divide (int, int) throw(Zerodivide);   // function may throw an exception</tt><tt>                                           // of type Zerodivide, but no other</tt></pre><p>If your function never throws any exceptions, it can be declared as follows:</p><pre><tt>bool equals (int, int) throw(); //no exception is thrown from this function</tt></pre><p>Note that a function that is declared without an exception specification such   as</p><pre><tt>bool equals (int, int);</tt></pre><h4> guarantees nothing about its exceptions: It might throw any exception, or   it might throw no exceptions. Exception Specifications Are Enforced At Runtime</h4><p>An exception specification may not be checked at compile time, </p><p>but rather at runtime. When a function attempts to throw an exception that   it is not allowed to throw according to its exception specification, the exception   handling mechanism detects the violation and invokes the standard function <tt>unexpected()</tt>.   The default behavior of <tt>unexpected()</tt> is to call <tt>terminate()</tt>,   which terminates the program. A violation of an exception specification is most   likely a bug, and should not occur -- this is why the default behavior is program   termination. The default behavior can be altered, nonetheless, by using the   function <tt>set_unexpected().</tt></p><p>Because exception specifications are enforced only at runtime, the compiler   might deliberately ignore code that seemingly violates exception specifications.   Consider the following:</p><pre><tt>int f();    // no exception specification, f can throw any type of exception</tt><tt>void g(int j) throw()    // g promises not to throw any exception at all</tt>

⌨️ 快捷键说明

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