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

📄 ch06.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<tt>void func(File&amp; );</tt><tt>int main()</tt><tt>{</tt><tt>  try //outer try</tt><tt>  {</tt><tt>    File f ("db.dat");</tt><tt>    func;   // 1</tt><tt>  }</tt><tt>catch(...) // 7</tt><tt>  //this handler will catch the re-thrown exception; </tt><tt>  //note: the same exception type is required</tt><tt>  {</tt><tt>    cout&lt;&lt;"re-thrown exception caught";</tt><tt>  }</tt><tt>  return 0;</tt><tt>}</tt><tt>void func(File &amp; f)</tt><tt>{</tt><tt>  try //inner try</tt><tt>  {</tt><tt>      if (f.IsValid() == false )</tt><tt>      throw FileException("db.dat");  // 2</tt><tt>  }</tt><tt>  catch(FileException &amp;fe) // 3</tt><tt>//first chance to cope with the exception</tt><tt>  {</tt><tt>    cout&lt;&lt;"invalid file specification" &lt;&lt;fe.Error()&lt;&lt;endl;</tt><tt>    if (f.OpenNew() != SUCCESS) (5)</tt><tt>     //re-throw the original exception and let a higher handler deal with it</tt><tt>    throw; // 6</tt><tt>  }</tt><tt>}</tt></pre><p>In the preceding example, the function <tt>func()</tt> is called from the <tt>try</tt>   block inside <tt>main()</tt> (1). The second <tt>try</tt> block inside <tt>func()</tt>   throws an exception of type <tt>FileException</tt> (2). This exception is caught   by the <tt>catch</tt> block inside <tt>func()</tt> (3). The <tt>catch</tt> block   attempts to remedy the situation by opening a new file. This attempt fails (5),   and the <tt>FileException</tt> is rethrown (6). Finally, the rethrown exception   is caught -- this time, by the <tt>catch(...)</tt> block inside <tt>main()</tt>   (7).</p><h3> <a name="Heading22">Function try Blocks</a></h3><p>A <i>function</i> <tt>try</tt><i> block</i> is a function whose body consists   of a <tt>try</tt> block and its associated handlers. A function <tt>try</tt>   block enables a handler to catch an exception </p><p>that is thrown during the execution of the <i>initializer expressions</i> in   the constructor's member initialization list or during the execution of the   constructor's body. Note, however, that unlike handlers of ordinary exceptions,   the handler of a function <tt>try</tt> block merely catches the exception --   it cannot continue the object's construction normally. This is because the partially   constructed object is destroyed as a result of the stack unwinding. In addition,   the handler of a function <tt>try</tt> block cannot execute a <tt>return</tt>   statement (eventually, the handler must exit by a <tt>throw</tt>). What is the   use of a function <tt>try</tt> block then? The handler enables you to throw   a different exception than the one that it just caught, thereby preventing a   violation of the exception specification. For example</p><pre><tt>class X{}; </tt><tt>C::C(const std::string&amp; s) throw (X) //  allowed to throw X only </tt><tt>try</tt><tt>: str(s) // str's constructor might throw a bad_alloc exception, </tt><tt>         // might violate C's exception specification</tt><tt>{</tt><tt>  // constructor function body</tt><tt>}</tt><tt>catch (...) //handle any exception thrown from ctor initializer or ctor body</tt><tt>{</tt><tt>  //... </tt><tt>  throw X(); //replace bad_alloc exception with an exception of type X</tt><tt>}</tt></pre><p>In this example, a <tt>string</tt> object is first constructed as a member   of class <tt>C</tt>. <tt>string</tt> might throw a <tt>bad_alloc</tt> exception   during its construction. The function try block catches the <tt>bad_alloc</tt>   exception and throws instead an exception of type <tt>X</tt>, which satisfies   the exception specification of <tt>C</tt>'s constructor.</p><h3> <a name="Heading23">Use auto_ptr&lt;&gt; to Avoid Memory Leaks</a></h3><p>The Standard Library supplies the class template <tt>auto_ptr&lt;&gt;</tt>   (discussed in Chapter 10, "STL and Generic Programming"), which automatically   deallocates memory that is allocated on the free store in much the same manner   as local objects are reclaimed in case of exiting their scope. When an <tt>auto_ptr&lt;&gt;</tt>   is instantiated, it can be initialized with a pointer to an object that is allocated   on the free store. When the current scope is exited, the destructor of the <tt>auto_ptr&lt;&gt;</tt>   object automatically deletes the object that is bound to it. By using <tt>auto_ptr&lt;&gt;</tt>,   you can avoid memory leakage in the case of an exception. Furthermore, <tt>auto_ptr&lt;&gt;</tt>   can simplify programming by sparing the bother of explicitly deleting objects   that were allocated on the free store. <tt>auto_ptr&lt;&gt;</tt> is defined   in the standard <tt>&lt;memory&gt;</tt> header file. </p><p>For example</p><pre><tt>#include &lt;memory&gt;</tt><tt>#include &lt;iostream&gt;</tt><tt>using namespace std;</tt><tt>class Date{ public: const char * DateString(); };</tt><tt>void DisplayDate()</tt><tt>{</tt><tt>        //create a local object of type auto_ptr&lt;Date&gt;</tt><tt>  auto_ptr&lt;Date&gt; pd (new Date); //now pd is owned by the template object</tt><tt>  cout&lt;&lt; pd-&gt; DateString();</tt><tt>  //pd is automatically deleted by the destructor of auto_ptr; </tt><tt>}</tt></pre><p>In the preceding example, the <tt>auto_ptr&lt;&gt;</tt> instance, <tt>pd</tt>,   can be used like an ordinary pointer to <tt>Date</tt>. The overloaded operators   <tt>*</tt>, <tt>-&gt;</tt>, and <tt>&amp;</tt> of <tt>auto_ptr&lt;&gt;</tt>   provide the pointer-like syntax. <tt>pd</tt>'s<tt> bound object </tt> is automatically   destroyed when <tt>DisplayDate()</tt> exits.</p><h2> <a name="Heading24"> Exception Handling Performance Overhead</a></h2><p>By nature, exception handling relies heavily on runtime type checking. When   an exception is thrown, the implementation has to determine whether the exception   was thrown from a <tt>try</tt> block (an exception can be thrown from a program   section that is not enclosed within a <tt>try</tt> block -- by operator <tt>new</tt>,   for example). If indeed the exception was thrown from a <tt>try</tt> block,   the implementation compares the type of the exception and attempts to find a   matching handler in the current scope. If a match is found, control is transferred   to the handler's body. This is the optimistic scenario. What if the implementation   cannot find a matching handler for the exception, though, or what if the exception   was not thrown from a <tt>try</tt> block? In such a case, the current function   is unwound from the stack and the next active function in the stack is entered.   The same process is reiterated until a matching handler has been found (at that   point, all the automatic objects that were created on the path from a <tt>try</tt>   block to a <tt>throw</tt> expression have been destroyed). When no matching   handler can be found in the program, <tt>terminate()</tt> is invoked and the   program terminates.</p><h3> <a name="Heading25">Additional Runtime Type Information</a></h3><p>The exception handling mechanism has to store additional data about the type   of every exception object and every <tt>catch</tt> statement in order to perform   the runtime matching between an exception and its matching handler. Because   an exception can be of any type, and because it can be polymorphic as well,   its dynamic type must be queried at runtime, using <i>runtime type information</i>   (<i>RTTI</i>). RTTI, imposes an additional overhead in terms of both execution   speed and program size (see Chapter 7, "Runtime Type Information"). Yet RTTI   alone is not enough. The implementation also requires runtime <i>code</i> information,   that is, information about the structure of each function. This information   is needed to determine whether an exception was thrown from a <tt>try</tt> block.   This information is generated by the compiler in the following way: The compiler   divides each function body into three parts: one that is outside a <tt>try</tt>   block with no active objects, a second part that is also outside a <tt>try</tt>   block but that has active objects that have to be destroyed during stack unwinding,   and a third part that is within a <tt>try</tt> block.</p><h3> <a name="Heading26">Toggling Exception Handling Support</a></h3><p>The technicalities of exception handling implementation vary among compilers   and platforms. In all of them, however, exception handling imposes additional   overhead even when no exception is ever thrown. The overhead lies in both execution   speed and program size. Some compilers enable you to toggle exception handling   support. When it is turned off, the additional data structures, lookup tables,   and auxiliary code are not generated. However, turning off exception handling   is rarely an option. Even if you do not use exceptions directly, you are probably   using them implicitly: Operator <tt>new</tt>, for example, might throw a <tt>std::bad_alloc</tt>   exception when it fails -- and so do other built-in operators; STL containers   might throw their own exceptions, and so might other functions of the Standard   Library. Code libraries that are supplied by third party vendors might use exceptions   as well. Therefore, you can safely turn off exception handling support only   when you are porting pure C code into a C++ compiler. As long as pure C code   is used, the additional exception handling overhead is unnecessary and can be   avoided.</p><h2> <a name="Heading27">Misuses of Exception Handling</a></h2><p>Exception handling is not confined to errors. Some programmers might use it   simply as an alternative control structure to <tt>for</tt> loops or <tt>while</tt>   and <tt>do</tt> blocks. For example, a simple application that prompts the user   to enter data until a certain condition has been fulfilled can be (rather naively)   implemented as follows:</p><pre><tt>#include &lt;iostream&gt;</tt><tt>using namespace std;</tt><tt>class Exit{}; //used as exception object</tt><tt>int main()</tt><tt>{</tt><tt> int num;</tt><tt> cout&lt;&lt; "enter a number; 99 to exit" &lt;&lt;endl;</tt><tt> try</tt><tt> {</tt><tt>   while (true) //infinitely</tt><tt>   {</tt><tt>     cin&gt;&gt;num;</tt><tt>     if (num == 99)</tt><tt>         throw Exit(); //exit the loop</tt><tt>     cout&lt;&lt; "you entered: " &lt;&lt; num &lt;&lt; "enter another number " &lt;&lt;endl;</tt><tt>   }</tt><tt> }</tt><tt> catch (Exit&amp; )</tt><tt> {</tt><tt>   cout&lt;&lt; "game over" &lt;&lt;endl;</tt><tt> }</tt><tt> return 0;</tt><tt>}</tt></pre><p>In the preceding example, the programmer locates an infinite loop within a   try block. The <tt>throw</tt> statement breaks the loop and transfers control   to the following <tt>catch</tt> statement. This style of programming is not   recommended, however. It is very inefficient due to the excess overhead of exception   handling. Furthermore, it is rather verbose and might have been much simpler   and shorter had it been written with a <tt>break</tt> statement. In demo apps   such as this one, the difference is mostly a stylistic one. In large-scale applications,   the use of exception handling as an alternative control structure imposes a   significant performance overhead.</p><p>Simple runtime errors that can be handled safely and effectively without the   heavy machinery of exception handling need to also be treated by traditional   methods. For example, a password entry dialog box should not throw an exception   if the user mistyped his or her password. It is much simpler to redisplay the   password entry dialog again with an appropriate error message. On the other   hand, if the user enters wrong passwords dozens of times in a row, this can   indicate a malicious break-in attempt. In this case, an exception should be   thrown. The appropriate handler can page the system administrator and security   officer. </p><h2> <a name="Heading28">Conclusions</a></h2><p>The exception handling mechanism of C++ overcomes the problems associated with   the traditional methods. It frees the programmer from writing tedious code that   checks the success status of every function call. Exception handling also eliminates   human mistakes. Another important advantage of exception handling is the automatic   unwinding of the stack, which ensures that local active objects are properly   destroyed and their resources are released.</p><p>Implementing an exception handling mechanism was not a trivial task. The need   to query the dynamic type of exception led to the introduction of RTTI into   C++. The additional overhead of exception handling derives from the RTTI data   structures, the "scaffolding" code that is generated by the compiler, and other   implementation-dependent factors. Exceptions can be grouped into categories;   the standard exception classes are a good example of this. In recent years,   a few loopholes in the exception handling mechanism have been fixed. The first   was the addition of exception specifications to functions' prototypes. The second   was the introduction of a function <tt>try</tt> block, which enables the program   to handle an exception that is thrown during the execution of the initializer   expressions in the constructor's member initialization list or during the execution   of the constructor's body.</p><p>Exception handling is a very powerful and flexible tool for handling runtime   errors effectively. However, use it judiciously.</p><CENTER><P><HR>  <A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR><BR><BR><p></P><P>&#169; <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. Allrights reserved.</p></CENTER></BODY></HTML>

⌨️ 快捷键说明

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