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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>

<HEAD>
	<META NAME="Author" Content="Steph Mineart">
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
	<TITLE>ANSI/ISO C++ Professional Programmer's Handbook - Chapter 6 -  Exception Handling</TITLE>
	<link rel="stylesheet"  TYPE="text/css" href="/includes/stylesheets/ebooks.css">
</head>

<BODY TEXT="#000000" BGCOLOR="#FFFFFF">

<CENTER>
<H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>
ANSI/ISO C++ Professional Programmer's Handbook</H1>
</CENTER>
<CENTER>
  <P><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> 
  <HR>
</CENTER>
<H1 align="center">6</H1>
<h1 align="center"> Exception Handling </h1>
<address>by Danny Kalev</address>

<ul>
  <li><a href="#Heading1">Introduction</a> 
  <li><a href="#Heading2">Traditional Error Handling Methods</a> 
    <ul>
      <li><a href="#Heading3">Returning an Error Code</a> 
      <li><a href="#Heading4">Turning on a Global Flag</a> 
      <li><a href="#Heading5">Terminating the Program</a> 
    </ul>
  <li><a href="#Heading6">Enter Exception Handling</a> 
    <ul>
      <li><a href="#Heading7">The Challenges of Implementation of Exception Handling</a> 
    </ul>
  <li><a href="#Heading8">Applying Exception Handling</a> 
    <ul>
      <li><a href="#Heading9">Exception Handling Constituents</a> 
      <li><a href="#Heading10">Stack Unwinding</a> 
      <li><a href="#Heading11">Passing Exception Objects to a Handler</a> 
      <li><a href="#Heading12">Exception Type Match</a> 
      <li><a href="#Heading13">Exceptions as Objects</a> 
      <li><a href="#Heading14">Exception Specification</a> 
    </ul>
  <li><a href="#Heading15">Exceptions During Object's Construction and Destruction</a> 
    <ul>
      <li><a href="#Heading16">Throwing Exceptions From A Destructor is Dangerous</a> 
    </ul>
  <li><a href="#Heading17">Global Objects: Construction and Destruction</a> 
  <li><a href="#Heading18">Advanced Exception Handling Techniques</a> 
    <ul>
      <li><a href="#Heading19">Standard Exceptions</a> 
      <li><a href="#Heading20">Exception Handlers Hierarchy</a> 
      <li><a href="#Heading21">Rethrowing an Exception</a> 
      <li><a href="#Heading22">Function try Blocks</a> 
      <li><a href="#Heading23">Use auto_ptr&lt;&gt; to Avoid Memory Leaks</a> 
    </ul>
  <li><a href="#Heading24"> Exception Handling Performance Overhead</a> 
    <ul>
      <li><a href="#Heading25">Additional Runtime Type Information</a> 
      <li><a href="#Heading26">Toggling Exception Handling Support</a> 
    </ul>
  <li><a href="#Heading27">Misuses of Exception Handling</a> 
  <li><a href="#Heading28">Conclusions</a> 
</ul>
<hr size=4>
<h2> <a name="Heading1">Introduction</a></h2>
<p>Large software applications are built in layers. At the lowest level, you usually 
  find library routines, API functions, and proprietary infrastructure functions. 
  At the highest level, there are user interface components that enable a user 
  to, for instance, fill out a data sheet in a spreadsheet application. Consider 
  an ordinary flight-booking application: its topmost layer consists of GUI components 
  that display contents on the user's screen. These high-level components interact 
  with data access objects, which in turn encapsulate database API routines. At 
  a lower level, the database API routines interact with the database engine. 
  The database engine itself invokes system services that deal with low-level 
  hardware resources such as physical memory, file system, and security modules. 
  In general, severe runtime errors are detected in these lower code layers, which 
  cannot -- or should not -- attempt to handle these errors on their own. The 
  handling of severe runtime errors is the responsibility of higher-level components. 
  In order to handle an error, however, higher-level components have to be informed 
  that an error has occurred. Essentially, error handling consists of detecting 
  an error and notifying the software components that are in charge. These components 
  in turn handle the error and attempt to recover from it.</p>
<h2> <a name="Heading2">Traditional Error Handling Methods</a></h2>
<p>In its earlier stages, C++ did not have a built-in facility for handling runtime 
  errors. Instead, the traditional C methods were used for that purpose. These 
  methods can be grouped into three design policies:</p>
<ul>
  <li>
    <p> Return a status code with agreed-upon values to indicate either success 
      or failure.</p>
  </li>
  <p></p>
  <li>
    <p> Assign an error code to a global variable and have other functions examine 
      it.</p>
  </li>
  <p></p>
  <li>
    <p> Terminate the program altogether.</p>
  </li>
</ul>
<p></p>
<p>Each of these methods has significant drawbacks and limitations in an object-oriented 
  environment. Some of them might be totally unacceptable, particularly in large-scale 
  applications. The following sections examine each of these methods more closely 
  in order to assess their inherent limitations and hazards.</p>
<h3> <a name="Heading3">Returning an Error Code</a></h3>
<p>To some extent, this method can be useful in small programs in which an agreed-upon, 
  closed set of error codes exists, and in which a rigorous policy of reporting 
  errors and checking the returned status of a function is applied. However, this 
  method has some noticeable limitations; for example, neither the error types 
  nor their enumerated values are standardized. Thus, in one library the implementer 
  might choose a return value of <tt>0</tt> (meaning <i>false</i>, perhaps) to 
  indicate an error, whereas another vendor might choose <tt>0</tt> to indicate 
  success and any nonzero value to indicate an error condition. Usually, the return 
  codes are shared in a common header file in the form of symbolic constants so 
  that some commonality can be maintained throughout an application or a development 
  team. These codes are not standardized, however.</p>
<p>Needless to say, the process of combining noncompatible software libraries 
  from different vendors or programmers becomes very difficult and confusing when 
  conflicting error codes are used. Another disadvantage is that every returned 
  code has to be looked up and interpreted -- a tedious and costly operation. 
  This policy requires that the return value of every function be checked every 
  time by every caller; failing to do so might lead to runtime disasters. When 
  an error code is detected, a return statement disrupts the normal execution 
  flow and passes the error code on to the caller. The additional code that wraps 
  every function call (to examine the return status and decide whether to continue 
  normally) can easily double the size of the program and cause serious difficulties 
  in the software's maintenance and readability. Worse yet, returning an error 
  value is sometimes impossible. For instance, constructors do not return values, 
  so they cannot use this method to report the failed construction of an object. 
</p>
<h3> <a name="Heading4">Turning on a Global Flag</a></h3>
<p>An alternative approach for reporting runtime errors is to use global flags, 
  which indicate whether the last operation ended successfully. Unlike the return 
  code policy, this method is standardized. The C &lt;errno.h&gt; header file 
  defines a mechanism for examining and assigning the value of a global integer 
  flag, <tt>errno</tt>. Note that the inherent drawbacks of this policy are not 
  negligible. In a multithreaded environment, the error code that is assigned 
  to <tt>errno</tt> by one thread can be inadvertently overwritten by another 
  thread before the caller has had a chance to examine it. In addition, the use 
  of an error code instead of a more readable message is disadvantageous because 
  the codes might not be compatible among different environments. Finally, this 
  method requires a well-disciplined programming style that relies on constant 
  checking of the current value of <tt>errno</tt>. </p>
<p>The global flag policy is similar to the function return value policy: Both 
  provide a mechanism for reporting an error, but neither guarantees that the 
  error is actually handled. For example, a function that fails to open a file 
  can indicate a failure by assigning an appropriate value to <tt>errno</tt>. 
  However, it cannot prevent another function from attempting to write into the 
  file or close it. Furthermore, if <tt>errno</tt> indicates an error and the 
  programmer detects and handles it as is expected, <tt>errno</tt> still has to 
  be reset explicitly. A programmer might forget to do so, thereby causing other 
  functions, which assume that the error has not been handled, to attempt to rectify 
  the problem -- with unpredictable results.</p>
<h3> <a name="Heading5">Terminating the Program</a></h3>
<p>The most drastic method of handling a runtime error is simply to terminate 
  the program immediately when a severe error has been detected. This solution 
  averts some of the drawbacks of the previous two methods; for example, there 
  is no need for repetitive examination of the status that is returned from every 
  function call, nor does the programmer have to assign a global flag, test its 
  value, and clear it in a repetitive and error-prone manner. The standard C library 
  has two functions that terminate a program: <tt>exit()</tt> and <tt>abort()</tt>. 
  <tt>exit()</tt> can be called to indicate successful termination of a program 
  (as the final statement in <tt>main()</tt>), or it can be called in the case 
  of a runtime error. Before returning control to the environment, <tt>exit()</tt> 
  first flushes open streams and closes open files. <tt>abort()</tt>, on the other 
  hand, indicates abnormal program termination. It terminates immediately, without 
  flushing streams or closing open files.</p>
<p>Critical applications cannot just halt abruptly on every occurrence of a runtime 
  error. It would be disastrous if a life support machine stopped functioning 
  just because its controller detected a division by zero; likewise, the embedded 
  computer that controls the automatic functions of a manned space shuttle should 
  not halt just because it temporarily loses communication with ground control. 
  Similarly, applications such as the billing system of a telephone company or 
  a banking application cannot break down altogether whenever a runtime exception 
  occurs. Robust, real world applications can -- and must -- do better than that.</p>
<p>Program termination is problematic even for applications, such as an operating 
  system, that are expected to abort in the case of serious runtime errors. A 
  function that detects the error usually does not have the necessary information 
  to estimate the severity of the error. A memory allocation function, for example, 
  cannot tell whether an allocation request has failed because the user is currently 
  using a debugger, a Web browser, a spreadsheet, and a word processor all at 
  once, or because the system has become unstable due to a severe hardware fault. 
  In the first scenario, the system can simply display a message, requesting that 
  the user close unnecessary applications. In the second scenario, a more drastic 
  measure might be required. Under this policy, however, the allocation function 
  simply aborts the program (the operating system kernel, in this case), regardless 
  of the severity of the error. This is hardly applicable in nontrivial applications. 
  Good system design has to ensure that runtime errors are detected and reported, 
  but it also has to ensure a minimal level of fault tolerance.</p>
<p>Terminating the program might be acceptable under extreme conditions or during 
  debugging phases. However, <tt>abort()</tt> and <tt>exit()</tt> are never to 
  be used in an object-oriented environment, even during debugging, because they 
  are unaware of the C++ object model.</p>
<h4> exit() and abort() Do Not Destroy Objects</h4>
<p>An object can hold resources that are acquired by the constructor or a member 
  function: memory allocated on the free store, file handles, communication ports, 
  database transaction locks, I/O devices, and so on. These resources have to 
  be properly released by the object that uses them when it's done. Usually, these 
  resources are released by the destructor. This design idiom is called <i>resource 
  initialization is acquisition</i> (this is discussed in greater detail in Chapter 
  5, "Object-Oriented Program and Design"). Local objects that are created on 
  the stack are destroyed automatically when their block or function exits. Neither 
  <tt>abort() nor exit()</tt>, however, invokes the destructors of local objects. 
  Therefore, an abrupt program termination caused by calling these functions can 
  cause irreversible damage: A database can be corrupted, files can be lost, and 
  valuable data can evaporate. For this reason, do not use either <tt>abort()</tt> 
  or <tt>exit()</tt> in an object-oriented environment.</p>
<h2> <a name="Heading6">Enter Exception Handling</a></h2>
<p>As you have observed, none of the traditional error handling methods of C are 
  adequate for C++; one of the goals of C++ was to enable better and safer large-scale 
  software development than is offered by C.</p>
<p>The designers of C++ were aware of the considerable difficulties resulting 
  from the lack of a proper error handling mechanism. They sought a solution that 
  was free from all the ailments of C's traditional error handling. The suggested 
  mechanism was based on the automatic transfer of control to the system when 
  an exception is triggered. The mechanism had to be simple, and it had to free 
  the programmer from the drudgery of constantly examining a global flag or the 
  returned value of a function. Additionally, it had to ensure that the code sections 
  that handle the exception are automatically informed when an exception occurs. 
  Finally, it had to ensure that when an exception is not handled locally, local 
  objects are properly destroyed and their resources are released before the exception 
  is propagated to a higher handler.</p>
<p>In 1989, after several years of research and a plethora of draft proposals, 
  exception handling was added to C++. C++ is not the first language to offer 
  structured runtime error handling support. Back in the 1960s, PL/1 offered a 
  built-in exception handling mechanism; Ada provided its own version of exception 
  handling in the early 1980s, as did several other languages. But none of these 
  exception handling models fit the C++ object model and program structure. Therefore, 
  the proposed exception handling for C++ was unique, and it has served as a model 
  for newer languages that have appeared since. </p>
<p>Implementing an exception handling mechanism turned out to be a real challenge. 
  The first C++ compiler, cfront, ran on UNIX. Like many UNIX compilers, it was 
  a translator that first transformed C++ code into C, and then compiled the resultant 
  C code. Release 4.0 of cfront was supposed to include exception handling. However, 
  the implementation of an exception handling mechanism that met all the requirements 
  got so complicated that the development team of cfront 4.0 decided to abandon 
  the project entirely after spending a whole year designing it. cfront 4.0 was 
  never released; however, exception handling became an integral part of Standard 
  C++. Other compilers that started to appear later supported it. The following 
  section explains why it was it so difficult to implement exception handling 

⌨️ 快捷键说明

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