📄 index.html
字号:
<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>
<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 <stdexcept>). 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 <stdexcept> header). This hierarchy enables the application
to catch these exceptions in a single <tt>catch</tt> statement:</p>
<pre>
<tt>catch (std::exception& 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 < ></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 <stdexcept> </tt>
<tt>#include <iostream></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& alloc_failure) // bad_alloc is </tt>
<tt> //derived from exception</tt>
<tt> {</tt>
<tt> cout<<"memory allocation failure";</tt>
<tt> //... handle exception thrown by operator new</tt>
<tt> }</tt>
<tt> catch(exception& std_ex) </tt>
<tt> {</tt>
<tt> cout<< std_ex.what() <<endl; </tt>
<tt> }</tt>
<tt> catch(...) // exceptions that are not handled elsewhere are caught here</tt>
<tt> {</tt>
<tt> cout<<"unrecognized exception"<<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& std_ex) //bad_alloc exception is always handled here </tt>
<tt>{</tt>
<tt> //...handle the exception </tt>
<tt>}</tt>
<tt>catch(std::bad_alloc& alloc_failure) //unreachable </tt>
<tt>{</tt>
<tt> cout<<"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 <iostream></tt>
<tt>#include <string></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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -