📄 mi14.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 14: Use exception specifications judiciously</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT>
var dingbase = "MI14_DIR.HTM";
var dingtext = "Item M14, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName = "M14: Use exception specifications judiciously" -->
<A NAME="6011"></A>
<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>
<P><A NAME="dingp1"></A><font ID="mititle">Item 14: Use exception specifications judiciously.</font><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="72138"></A>
<P><A NAME="dingp2"></A><A NAME="66651"></A>There's no denying it: exception specifications have appeal. They make code easier to understand, because they explicitly state what exceptions a function may throw. But they're more than just fancy comments. Compilers are sometimes able to detect inconsistent exception specifications during compilation. Furthermore, if a function throws an exception not listed in its exception specification, that fault is detected at runtime, and the special function <CODE>unexpected</CODE> is automatically invoked. Both as a documentation aid and as an enforcement mechanism for constraints on exception usage, then, exception specifications seem <NOBR>attractive.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp3"></A><A NAME="44170"></A>
As is often the case, however, beauty is only skin deep. The default behavior for <CODE>unexpected</CODE> is to call <CODE>terminate</CODE>, and the default behavior for <CODE>terminate</CODE> is to call <CODE>abort</CODE>, so the default behavior for a program with a violated exception specification is to halt. Local variables in active stack frames are not destroyed, because <CODE>abort</CODE> shuts down program execution without performing such cleanup. A violated exception specification is therefore a cataclysmic thing, something that should almost never <NOBR>happen.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P> <P><A NAME="dingp4"></A><A NAME="44195"></A>
<A NAME="p73"></A>Unfortunately, it's easy to write functions that make this terrible thing occur. Compilers only <I>partially</I> check exception usage for consistency with exception specifications. What they do not check for — what the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver = "self.status = 'The latest publicly available C++ standard'; return true" onMouseOut = "self.status = self.defaultStatus" target="_top">language</NOBR> standard</A> <I>prohibits</I> them from rejecting (though they may issue a warning) — is a call to a function that <I>might</I> violate the exception specification of the function making the <NOBR>call.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P> <P><A NAME="dingp5"></A><A NAME="44606"></A>
Consider a declaration for a function <CODE>f1</CODE> that has no exception specification. Such a function may throw any kind of <NOBR>exception:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P><A NAME="44197"></A>
<UL><PRE>extern void f1(); // might throw anything
</PRE>
</UL><P><A NAME="44199"></A>
<A NAME="dingp6"></A>Now consider a function <CODE>f2</CODE> that claims, through its exception specification, it will throw only exceptions of type <CODE>int</CODE>:<SCRIPT>create_link(6);</SCRIPT>
</P><A NAME="44201"></A>
<UL><PRE>void f2() throw(int);
</PRE>
</UL><P><A NAME="dingp7"></A><A NAME="44203"></A>
It is perfectly legal C++ for <CODE>f2</CODE> to call <CODE>f1</CODE>, even though <CODE>f1</CODE> might throw an exception that would violate <CODE>f2</CODE>'s exception <NOBR>specification:<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P><A NAME="44204"></A>
<UL><PRE>void f2() throw(int)
{
...
f1(); // legal even though f1 might throw
// something besides an int
...
}
</PRE>
</UL><P><A NAME="dingp8"></A><A NAME="44712"></A>
This kind of flexibility is essential if new code with exception specifications is to be integrated with older code lacking such <NOBR>specifications.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P> <P><A NAME="dingp9"></A><A NAME="44235"></A>
Because your compilers are content to let you call functions whose exception specifications are inconsistent with those of the routine containing the calls, and because such calls might result in your program's execution being terminated, it's important to write your software in such a way that these kinds of inconsistencies are minimized. A good way to start is to avoid putting exception specifications on templates that take type arguments. Consider this template, which certainly looks as if it couldn't throw any <NOBR>exceptions:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P><A NAME="44270"></A>
<UL><PRE>// a poorly designed template wrt exception specifications
template<class T>
bool operator==(const T& lhs, const T& rhs) throw()
{
return &lhs == &rhs;
}
</PRE>
</UL><P><A NAME="dingp10"></A><A NAME="44269"></A>
This template defines an <CODE>operator==</CODE> function for all types. For any pair of objects of the same type, it returns <CODE>true</CODE> if the objects have the same address, otherwise it returns <CODE>false</CODE>.<SCRIPT>create_link(10);</SCRIPT>
</P> <P><A NAME="dingp11"></A><A NAME="44276"></A>
<A NAME="p74"></A>This template contains an exception specification stating that the functions generated from the template will throw no exceptions. But that's not necessarily true, because it's possible that <CODE>operator&</CODE> (the address-of operator — see <a href="../EC/EI45_FR.HTM#8160" TARGET="_top">Item E45</A>) has been overloaded for some types. If it has, <CODE>operator&</CODE> may throw an exception when called from inside <CODE>operator==</CODE>. If it does, our exception specification is violated, and off to <CODE>unexpected</CODE> we <NOBR>go.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P> <P><A NAME="dingp12"></A><A NAME="44299"></A>
This is a specific example of a more general problem, namely, that there is no way to know <I>anything</I> about the exceptions thrown by a template's type parameters. We can almost never provide a meaningful exception specification for a template, because templates almost invariably use their type parameter in some way. The conclusion? Templates and exception specifications don't <NOBR>mix.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P> <P><A NAME="dingp13"></A><A NAME="44306"></A>
A second technique you can use to avoid calls to <CODE>unexpected</CODE> is to omit exception specifications on functions making calls to functions that themselves lack exception specifications. This is simple common sense, but there is one case that is easy to forget. That's when allowing users to register callback <NOBR>functions:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P><A NAME="66726"></A>
<UL><PRE>// Function pointer type for a window system callback
// when a window system event occurs
typedef void (*CallBackPtr)(int eventXLocation,
int eventYLocation,
void *dataToPassBack);
</PRE>
</UL><A NAME="66727"></A>
<UL><PRE>// Window system class for holding onto callback
// functions registered by window system clients
class CallBack {
public:
CallBack(CallBackPtr fPtr, void *dataToPassBack)
: func(fPtr), data(dataToPassBack) {}
</PRE>
</UL><A NAME="66728"></A>
<UL><PRE> void makeCallBack(int eventXLocation,
int eventYLocation) const throw();
</PRE>
</UL><A NAME="66729"></A>
<UL><PRE>private:
CallBackPtr func; // function to call when
// callback is made
</PRE>
</UL><A NAME="66784"></A>
<UL><PRE>
void *data; // data to pass to callback
}; // function
</PRE>
</UL><A NAME="66786"></A>
<UL><PRE>// To implement the callback, we call the registered func-
// tion with event's coordinates and the registered data
void CallBack::makeCallBack(int eventXLocation,
int eventYLocation) const throw()
{
func(eventXLocation, eventYLocation, data);
}
</PRE>
</UL><P><A NAME="dingp14"></A><A NAME="66712"></A>
<A NAME="p75"></A>Here the call to <CODE>func</CODE> in <CODE>makeCallBack</CODE> runs the risk of a violated exception specification, because there is no way of knowing what exceptions <CODE>func</CODE> might <NOBR>throw.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P> <P><A NAME="dingp15"></A><A NAME="9589"></A>
This problem can be eliminated by tightening the exception specification in the <CODE>CallBackPtr</CODE> typedef:<a href="#9602"><sup>5</sup></A><SCRIPT>create_link(15);</SCRIPT>
</P><A NAME="9594"></A>
<UL><PRE>
typedef void (*CallBackPtr)(int eventXLocation,
int eventYLocation,
void *dataToPassBack) throw();
</PRE>
</UL><P><A NAME="dingp16"></A><A NAME="44347"></A>
Given this typedef, it is now an error to register a <CODE>callback</CODE> function that fails to guarantee it throws <NOBR>nothing:<SCRIPT>create_link(16);</SCRIPT>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -