📄 mi14.htm
字号:
</NOBR></P><A NAME="44348"></A>
<UL><PRE>
// a callback function without an exception specification
void callBackFcn1(int eventXLocation, int eventYLocation,
void *dataToPassBack);
</PRE>
</UL><A NAME="66844"></A>
<UL><PRE>void *callBackData;
</PRE>
</UL><A NAME="66845"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="44349"></A>
<UL><PRE>
CallBack c1(callBackFcn1, callBackData);
// error! callBackFcn1
// might throw an exception
</PRE>
</UL><A NAME="66825"></A>
<UL><PRE>// a callback function with an exception specification
void callBackFcn2(int eventXLocation,
int eventYLocation,
void *dataToPassBack) throw();
</PRE>
</UL><A NAME="44354"></A>
<UL><PRE>
CallBack c2(callBackFcn2, callBackData);
// okay, callBackFcn2 has a
// conforming ex. spec.
</PRE>
</UL><P><A NAME="dingp17"></A><A NAME="44335"></A>
This checking of exception specifications when passing function pointers is a relatively recent addition to the language, so don't be surprised if your compilers don't yet support it. If they don't, it's up to you to ensure you don't make this kind of <NOBR>mistake.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P> <P><A NAME="dingp18"></A><A NAME="44367"></A>
A third technique you can use to avoid calls to <CODE>unexpected</CODE> is to handle exceptions "the system" may throw. Of these exceptions, the most common is <CODE>bad_alloc</CODE>, which is thrown by <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE> when a memory allocation fails (see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>). If you use the <CODE>new</CODE> operator (again, see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>) in any function, you must be prepared for the possibility that the function will encounter a <CODE>bad_alloc</CODE> <NOBR>exception.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P><A NAME="44383"></A>
<P><A NAME="dingp19"></A>
Now, an ounce of prevention may be better than a pound of cure, but sometimes prevention is hard and cure is easy. That is, sometimes it's easier to cope with unexpected exceptions directly than to prevent them from arising in the first place. If, for example, you're writing soft<A NAME="p76"></A>ware that uses exception specifications rigorously, but you're forced to call functions in libraries that don't use exception specifications, it's impractical to prevent unexpected exceptions from arising, because that would require changing the code in the <NOBR>libraries.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P><A NAME="44398"></A>
<P><A NAME="dingp20"></A>
If preventing unexpected exceptions isn't practical, you can exploit the fact that C++ allows you to replace unexpected exceptions with exceptions of a different type. For example, suppose you'd like all unexpected exceptions to be replaced by <CODE>UnexpectedException</CODE> objects. You can set it up like <NOBR>this,<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="82709"></A>
<UL><PRE>
class UnexpectedException {}; // all unexpected exception
// objects will be replaced
// by objects of this type
<A NAME="44396"></A>
void convertUnexpected() // function to call if
{ // an unexpected exception
throw UnexpectedException(); // is thrown
}
</PRE>
</UL><A NAME="82737"></A>
<P><A NAME="dingp21"></A>and make it happen by <CODE></CODE>replacing the default <CODE>unexpected</CODE> function with <CODE>convertUnexpected</CODE>:<SCRIPT>create_link(21);</SCRIPT>
</P>
<A NAME="44406"></A>
<UL><PRE>set_unexpected(convertUnexpected);
</PRE>
</UL><A NAME="82668"></A>
<P><A NAME="dingp22"></A>
Once you've done this, any unexpected exception results in <CODE>convertUnexpected</CODE> being called. The unexpected exception is then replaced by a new exception of type <CODE>UnexpectedException</CODE>. Provided the exception specification that was violated includes <CODE>UnexpectedException</CODE>, exception propagation will then continue as if the exception specification had always been satisfied. (If the exception specification does not include <CODE>UnexpectedException</CODE>, <CODE>terminate</CODE> will be called, just as if you had never replaced <CODE>unexpected</CODE>.)<SCRIPT>create_link(22);</SCRIPT>
</P><A NAME="82748"></A>
<P><A NAME="dingp23"></A>
Another way to translate unexpected exceptions into a well known type is to rely on the fact that if the <CODE>unexpected</CODE> function's replacement rethrows the current exception, that exception will be replaced by a new exception of the standard type <CODE>bad_exception</CODE>. Here's how you'd arrange for that to <NOBR>happen:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="82761"></A>
<UL><PRE>
void convertUnexpected() // function to call if
{ // an unexpected exception
throw; // is thrown; just rethrow
} // the current exception
<A NAME="82757"></A>
set_unexpected(convertUnexpected);
// install convertUnexpected
// as the unexpected
// replacement
</PRE>
</UL><A NAME="82738"></A>
<A NAME="p77"></A><P><A NAME="dingp24"></A>
If you do this and you include <CODE>bad_exception</CODE> (or its base class, the standard class <CODE>exception</CODE>) in all your exception specifications, you'll never have to worry about your program halting if an unexpected exception is encountered. Instead, any wayward exception will be replaced by a <CODE>bad_exception</CODE>, and that exception will be propagated in the stead of the original <NOBR>one.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P><A NAME="44438"></A>
<P><A NAME="dingp25"></A>
By now you understand that exception specifications can be a lot of trouble. Compilers perform only partial checks for their consistent usage, they're problematic in templates, they're easy to violate inadvertently, and, by default, they lead to abrupt program termination when they're violated. Exception specifications have another drawback, too, and that's that they result in <CODE>unexpected</CODE> being invoked even when a higher-level caller is prepared to cope with the exception that's arisen. For example, consider this code, which is taken almost verbatim from <A HREF="./MI11_FR.HTM#39749" TARGET="_top">Item 11</A>:<SCRIPT>create_link(25);</SCRIPT>
</P>
<A NAME="44476"></A>
<UL><PRE>
class Session { // for modeling online
public: // sessions
~Session();
...
<A NAME="57532"></A>
private:
static void logDestruction(Session *objAddr) throw();
};
<A NAME="44458"></A>
Session::~Session()
{
try {
logDestruction(this);
}
catch (...) { }
}
</PRE>
</UL><A NAME="44456"></A>
<P><A NAME="dingp26"></A>
The <CODE>Session</CODE> destructor calls <CODE>logDestruction</CODE> to record the fact that a <CODE>Session</CODE> object is being destroyed, but it explicitly catches any exceptions that might be thrown by <CODE>logDestruction</CODE>. However, <CODE>logDestruction</CODE> comes with an exception specification asserting that it throws no exceptions. Now, suppose some function called by <CODE>logDestruction</CODE> throws an exception that <CODE>logDestruction</CODE> fails to catch. This isn't supposed to happen, but as we've seen, it isn't difficult to write code that leads to the violation of exception specifications. When this unanticipated exception propagates through <CODE>logDestruction</CODE>, <CODE>unexpected</CODE> will be called, and, by default, that will result in termination of the program. This is correct behavior, to be sure, but is it the behavior the author of <CODE>Session</CODE>'s destructor wanted? That author took pains to handle <I>all possible</I> exceptions, so it seems almost unfair to halt the program without giving <CODE>Session</CODE>'s destructor's <CODE>catch</CODE> block a chance to work. If <CODE>logDestruction</CODE> had no exception specification, <A NAME="p78"></A>this I'm-willing-to-catch-it-if-you'll-just-give-me-a-chance scenario would never arise. (One way to prevent it is to replace <CODE>unexpected</CODE> as described <NOBR>above.)<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P><A NAME="44538"></A>
<P><A NAME="dingp27"></A>
It's important to keep a balanced view of exception specifications. They provide excellent documentation on the kinds of exceptions a function is expected to throw, and for situations in which violating an exception specification is so dire as to justify immediate program termination, they offer that behavior by default. At the same time, they are only partly checked by compilers and they are easy to violate inadvertently. Furthermore, they can prevent high-level exception handlers from dealing with unexpected exceptions, even when they know how to. That being the case, exception specifications are a tool to be applied judiciously. Before adding them to your functions, consider whether the behavior they impart to your software is really the behavior you <NOBR>want.<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI13_FR.HTM" TARGET="_top">Item 13: Catch exceptions by reference</A> <BR> Continue to <A HREF="./MI15_FR.HTM" TARGET="_top">Item 15: Understand the costs of exception handling</A></FONT></DIV>
<A NAME="9602"></A>
<HR WIDTH="100%">
<A NAME="dingp28"></A>
<SUP>5</SUP> Alas, it can't, at least not portably. Though many compilers accept the code shown on this page, the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver="self.status='ISO/ANSI Standatdization Committee Home Page'; return true" onMouseOut="self.status=self.defaultStatus" target="_top">standardization</NOBR> committee</A> has inexplicably decreed that "an exception specification shall not appear in a <CODE>typedef</CODE>." I don't know why. If you need a portable solution, you must — it hurts me to write this — make <CODE>CallBackPtr</CODE> a macro, sigh.<SCRIPT>create_link(28);</SCRIPT>
<BR>
<A HREF="#9589">Return</A>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -