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

📄 ch06.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<tt>{</tt><tt>  int result = f(); // if f throws an exception, g will violate its guarantee</tt><tt>                    //not to throw an exception. still, this code is legal</tt><tt>}</tt></pre><p>In this example, the function <tt>g()</tt>, which is not allowed to throw any   exception, invokes the function <tt>f()</tt>. <tt>f()</tt>, however, is free   to throw any exception because it has no exception specification. If <tt>f()</tt>   throws an exception, it propagates through <tt>g()</tt>, thereby violating <tt>g()</tt>'s   guarantee not to throw any exception.It might seem surprising that exception   specifications are enforced only at runtime because at least some of the violations   can be caught at compile time and flagged as errors. This is not the case, however.   There are several compelling reasons for the runtime checking policy.. In the   preceding example, <tt>f()</tt> can be a legacy C function. It is impossible   to enforce every C function to have an exception specification. Forcing the   programmer to write unnecessary <tt>try</tt> and <tt>catch(...)</tt> blocks   in <tt>g()</tt> "just in case" is impractical as well -- what if the programmer   knows that <tt>f()</tt> doesn't throw any exception at all and the code is therefore   safe? By enforcing exception specifications at runtime, C++ applies the "trust   the programmer" policy instead of forcing an unnecessary burden on both the   programmer and the implementation.</p><h4> Concordance of Exception Specification</h4><p>C++ requires exception specification concordance in derived classes. This means   that an overriding virtual function in a derived class has to have an exception   specification that is at least as restrictive as the exception specification   of the overridden function in the base class. For example</p><pre><tt>// various exception classes</tt><tt>class BaseEx{};</tt><tt>class DerivedEx: public BaseEx{};</tt><tt>class OtherEx {};</tt><tt>class A</tt><tt>{</tt><tt>public:</tt><tt>  virtual void f() throw (BaseEx);</tt><tt>  virtual void g() throw (BaseEx);</tt><tt>  virtual void h() throw (DerivedEx);</tt><tt>  virtual void i() throw (DerivedEx);</tt><tt>  virtual void j() throw(BaseEx);</tt><tt>};</tt><tt>class D: public A</tt><tt>{</tt><tt>public: </tt><tt>  void f() throw (DerivedEx); //OK, DerivedEx is derived from BaseEx</tt><tt>class D: public A</tt><tt>{</tt><tt>public: </tt><tt>  void f() throw (DerivedEx); //OK, DerivedEx is derived from BaseEx</tt><tt>  void g() throw (OtherEx);  //error; exception specification is </tt><tt>                             //incompatible with A's</tt><tt>  void h() throw (DerivedEx); //OK, identical to the exception </tt><tt>                              //specification in base</tt><tt>  void i() throw (BaseEx); //error, BaseEx is not a DerivedEx nor is it</tt><tt>                           //derived from DerivedEx</tt><tt>  void j()  throw (BaseEx,OtherEx); //error, less restrictive than the</tt><tt>                                    //specification of A::j</tt><tt>};</tt><tt>};</tt></pre><p>The same concordance restrictions apply to pointers to functions. A pointer   to a function that has an exception specification can be assigned only to a   function that has an identical or a more restrictive exception specification.   This implies that a pointer to function that has no exception specification   cannot be assigned to a function that has one. Note, however, that an exception   specification is not considered part of the function type. Therefore, you cannot   declare two distinct functions that differ only in their exception specification.   For example</p><pre><tt>void f(int) throw (Y);</tt><tt>void f(int) throw (Z); //error; redefinition of 'void f(int)'</tt></pre><p>For the same reason, declaring a <tt>typedef</tt> that contains an exception   specification is also an error:</p><pre><tt>typedef void (*PF) (int) throw(Exception); // error</tt></pre><h2> <a name="Heading15">Exceptions During Object's Construction and Destruction</a></h2><p>Constructors and destructors are invoked automatically; in addition, they cannot   return values to indicate a runtime error. Seemingly, the most plausible way   of reporting runtime errors during object construction and destruction is by   throwing an exception. However, there are additional factors that you have to   consider before throwing an exception in these cases. You should be particularly   cautious about throwing an exception from a destructor.</p><h3> <a name="Heading16">Throwing Exceptions From A Destructor is Dangerous</a></h3><p>Throwing an exception from a destructor is not recommended. The problem is   that a destructor might be invoked due to another exception as part of the stack   unwinding. If a destructor that was invoked due to another exception also throws   an exception of its own, the exception handling mechanism invokes <tt>terminate()</tt>.   If you really have to throw an exception from a destructor, it is advisable   to check first whether another uncaught exception is currently being processed. </p><h4> Checking for an Uncaught Exception</h4><p>A thrown exception is considered caught when its corresponding handler has   been entered (or, if such a handler cannot be found, when the function <tt>unexpected()</tt>   has been invoked). In order to check whether a thrown exception is currently   being processed, you can use the standard function <tt>uncaught_exception()</tt>   (which is defined in the standard header &lt;stdexcept&gt;). For example</p><pre><tt>class FileException{};</tt><tt>File::~File() throw (FileException)</tt><tt>{</tt><tt>   if ( close(file_handle) != success) // failed to close current file?</tt><tt>  {</tt><tt>    if (uncaught_exception()  == true ) // is there any uncaught exception </tt><tt>                                        //being processed currently?</tt><tt>       return;  // if so, do not throw an exception</tt><tt>    throw FileException(); // otherwise, it is safe to throw an exception</tt><tt>                           // to signal an error</tt><tt>  }</tt><tt>  return; // success</tt><tt>}</tt></pre><p>Still, a better design choice is to handle exceptions within a destructor rather   than let them propagate into the program. For example</p><pre><tt>void cleanup() throw (int);</tt><tt>class C </tt><tt>{</tt><tt>public:</tt><tt>  ~C();</tt><tt>};</tt><tt>C::~C() </tt><tt>{</tt><tt>  try </tt><tt>  {</tt><tt>    cleanup();</tt><tt>  }</tt><tt>  catch(int) </tt><tt>  {</tt><tt>    //handle the exception within the destructor</tt><tt>  }</tt><tt>}</tt></pre><p>If an exception is thrown by <tt>cleanup()</tt>, it is handled inside the destructor.   Otherwise, the thrown exception will propagate outside the destructor, and if   the destructor has been invoked while unwinding the stack due to another exception,   <tt>terminate()</tt> will be called.</p><h2> <a name="Heading17">Global Objects: Construction and Destruction</a></h2><p>Conceptually, the construction of global objects takes place before program   outset. Therefore, any exception that is thrown from a constructor of a global   object can never be caught. This is also true for a global object's destructor   -- the destruction of a global object executes after a program's termination.   Hence, an exception that is thrown from a global object's destructor cannot   be handled either. </p><h2> <a name="Heading18">Advanced Exception Handling Techniques</a></h2><p>The simple <tt>try</tt>-<tt>throw</tt>-<tt>catch</tt> model can be extended   even further to handle more complicated runtime errors. This section discusses   some of the more advanced uses of exception handling, including exception hierarchies,   rethrowing exceptions, function <tt>try</tt> blocks and the <tt>auto_ptr</tt>   class. </p><h3> <a name="Heading19">Standard Exceptions</a></h3><p>C++ defines a hierarchy of standard exceptions that are thrown at runtime when   abnormal conditions arise. The standard exception classes are derived from <tt>std::exception</tt>   (defined in the &lt;stdexcept&gt; header). This hierarchy enables the application   to catch these exceptions in a single <tt>catch</tt> statement:</p><pre><tt>catch (std::exception&amp; exc)</tt><tt>{</tt><tt>  // handle exception of type std::exception as well as </tt><tt>  //any exception derived from it</tt><tt>}</tt></pre><p>The standard exceptions that are thrown by built-in operators of the language   are</p><pre><tt>std::bad_alloc     //by operator new</tt><tt>std::bad_cast     //by operator dynamic_cast &lt; &gt;</tt><tt>std::bad_typeid   //by operator typeid</tt><tt>std::bad_exception   //thrown when an exception specification of </tt></pre><p> //a function is violatedAll standard exceptions have provided the member function   <tt>what()</tt>, which returns a <tt>const char *</tt> with an implementation-dependent   verbal description of the exception. Note, however, that the standard library   has an additional set of exceptions that are thrown by its components.</p><h3> <a name="Heading20">Exception Handlers Hierarchy</a></h3><p>Exceptions are caught in a bottom-down hierarchy: Specific (most derived classes)   exceptions are handled first, followed by groups of exceptions (base classes),   and, finally, a <tt>catch all</tt> handler. For example</p><pre><tt>#include &lt;stdexcept&gt;  </tt><tt>#include &lt;iostream&gt;</tt><tt>using namespace std;</tt><tt>int main()</tt><tt>{</tt><tt>  try</tt><tt>  {</tt><tt>      char * buff = new char[100000000];</tt><tt>      //...use buff</tt><tt>  }</tt><tt>  catch(bad_alloc&amp; alloc_failure)   // bad_alloc is </tt><tt>                                         //derived from exception</tt><tt>  {</tt><tt>    cout&lt;&lt;"memory allocation failure";</tt><tt>    //... handle exception thrown by operator new</tt><tt>  }</tt><tt>  catch(exception&amp; std_ex) </tt><tt>  {</tt><tt>    cout&lt;&lt; std_ex.what() &lt;&lt;endl; </tt><tt>  }</tt><tt>  catch(...)  // exceptions that are not handled elsewhere are caught here</tt><tt>  {</tt><tt>    cout&lt;&lt;"unrecognized exception"&lt;&lt;endl;</tt><tt>  }</tt><tt>  return 0;</tt><tt>}</tt></pre><p>Handlers of the most derived objects must appear before the handlers of base   classes. This is because handlers are tried in order of appearance. It is therefore   possible to write handlers that are never executed, for example, by placing   a handler for a derived class after a handler for a corresponding base class.   For example</p><pre><tt>catch(std::exception&amp; std_ex) //bad_alloc exception is always handled here   </tt><tt>{</tt><tt>  //...handle the exception </tt><tt>}</tt><tt>catch(std::bad_alloc&amp; alloc_failure)   //unreachable </tt><tt>{</tt><tt>  cout&lt;&lt;"memory allocation failure";</tt><tt>}</tt></pre><h3> <a name="Heading21">Rethrowing an Exception</a></h3><p>An exception is thrown to indicate an abnormal state. The first handle to catch   the exception can try to fix the problem. If it fails to do so, or if it only   manages to perform a partial recovery, it can still rethrow the exception, thereby   letting a higher <tt>try</tt> block handle it. For that purpose, <tt>try</tt>   blocks can be nested in a hierarchical order, enabling a rethrown exception   from a lower <tt>catch</tt> statement to be caught again. A rethrow is indicated   by a <tt>throw</tt> statement without an operand. For example</p><pre><tt>#include &lt;iostream&gt;</tt><tt>#include &lt;string&gt;</tt><tt>using namespace std;</tt><tt>enum {SUCCESS, FAILURE};</tt><tt>class File</tt><tt>{</tt><tt>  public: File (const char *) {}</tt><tt>  public: bool IsValid() const {return false; }</tt><tt>  public: int OpenNew() const {return FAILURE; }</tt><tt>};</tt><tt>class Exception {/*..*/}; //general base class for exceptions</tt><tt>class FileException: public Exception</tt><tt>{</tt><tt>  public: FileException(const char *p) : s(p) {}</tt><tt>  public: const char * Error() const { return s.c_str(); }</tt><tt>  private: string s;</tt><tt>};</tt>

⌨️ 快捷键说明

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