可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
In C++ you may declare function with exception specification like this:
int foo() const throw(Exception);
I found those two links:
- http://www.cplusplus.com/doc/tutorial/exceptions/ and
- http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr156.htm
But several things end up unanswered...
Question 1: why to add exception specification? Will it bring any performance increase? What will be different for compiler? Because it seems just like an information for programmer to me.
Question 2: what will happend (what should happen) if I throw something that isn't in specification? For example:
int foo() throw(int) {
throw char; // Totally unrelated classes, not types in real
}
Question 3: function/method shouldn't throw anything. I found at least two (three, alternative syntax for different compilers) ways to specify no exception throwing:
int foo() throw();
int foo() __attribute(nothrow)__
for gcc
int foo() nothrow
for visual C++
Which one is "correct"? Is there any difference? Which one should I use?
Question 4: "standart exceptions", bad_alloc
,bad_cast
,bad_exception
,bad_typeid
and ios_base::failure
.
Ok bad_alloc
is self explaining and I know how (and more importantly when) to use it (add to exception specification), but what about the others? None of them does really ring a bell... Which "code pieces" are they associated with? Like bad_alloc
is associated with new char[500000]
.
Question 5: If I have exception classes hierarchy, like this:
class ExceptionFileType {
virtual const char * getError() const = 0;
};
class ExceptionFileTypeMissing : public ExceptionFileType {
virtual const char *getError() cosnt {
return "Missing file";
}
}
Should I use:
int foo() throw(ExceptionFileType);
Or:
int foo() throw(ExceptionFileTypeMissing,ExceptionFileTypeNotWritable,ExceptionFileTypeNotReadable,...)
Note: answers with references would be great. I'm looking for good practice tips.
回答1:
The simple "good practice" tip is: don't use exception specifications.
Essentially the only exception to that is the possibility of an empty exception specification: throw()
. That's sufficiently useful that in C++11 it's been given its own keyword (noexcept
). It's generally agreed that any non-empty exception specification is a lousy idea though.
Exception specifications (other than noexcept
) are officially deprecated -- and unlike many deprecated features, removing this would affect little enough source code that I think there's a good chance it really will eventually be removed (certainly no guarantee, but a pretty fair chance anyway).
As for what happens when/if you do throw an exception of a type not allowed by the exception specification: std::unexpected()
gets invoked. By default, that invokes terminate()
. You can use std::set_unexpected
to set your own handler -- but about all you can reasonably do is add some logging before you terminate()
. Your unexpected handler is not allowed to return.
回答2:
Question 1
Don't bother. They were a bad idea, and were deprecated in the latest version of the language. They give no benefit to the compiler, since they are checked at runtime; if anything, they might hurt performance in some cases.
Question 2
A function called std::unexpected
will be called. By default, this calls std::terminate
; by default, that terminates the program. Both of these behaviours can be changed, using std::set_unexpected
and std::set_terminate
to install your own handler, if you really want to.
Question 3
throw()
was the standard way to do it; the others are non-portable compiler extensions. In C++11, you might use noexcept
, which gives a compile-time check that nothing can throw, rather than a run-time check that nothing does throw.
Question 4
bad_cast
is thrown when a dynamic_cast
of a reference fails.
bad_exception
is thrown in some weird circumstances when an exception specification is violated.
bad_typeid
is thrown if evaluating the argument to typeid
involves dereferencing a null pointer
ios_base::failure
is thrown by the input/output library (<iostream>
etc.) when some operations fail
Question 5
If you want to allow the entire heirarchy to be thrown, then just specify the base class. But you shouldn't be using exception specifiers at all.
回答3:
First, let's be very clear what an exception specification does: it's
more or less like an assert
that can't be disabled, asserting that you
will not exit the function with an exception other than the ones
missing. As such, it's utility is far more limited than it would seem
at first; for the most part (and in this case, I can't imagine an
exception), the only really useful guarantee is throw()
, which
guarantees that no exception will be thrown; if you want to write
exception safe code, you'll need this guarantee for a few low level
functions.
In practice, although throw()
could allow some additional compiler
optimizations, the generic implementation tends to result in less
efficient code when an exception specification is used. In C++11, the
throw()
has been replaced by noexcept
, presumably with the hope that
compiler implementers will do something intelligent with it.
EDIT:
Since everyone (including myself) seems to have missed your question 4:
bad_alloc
will be thrown by the operator new
function if it cannot
allocate the memory.
bad_cast
will be thrown by a dynamic_cast
to a reference, in the
case where the cast fails. (A dynamic_cast
to a pointer returns a
null pointer in such cases.)
bad_exception
will be thrown when an exception specification is
violated, provided the exception specification allows bad_exception
.
(In other words, forget it.)
bad_typeid
will be thrown if you try to use typeid
with a null
pointer.
ios_base::failure
will be thrown if you request a stream to throw in
case of errors.
Practically speaking: you'll catch bad_alloc
if you want to recover
and continue from out of memory situations. Which means not very often.
(It's very, very difficult to recover from an out of memory situation.)
For bad_cast
, it's probably preferrable to use pointers, and test for
null, if you aren't sure. And there's no excuse for ever seeing a
bad_typeid
. Most of the time, you'll probably want to test IO errors
explicitly, rather than configuring the stream to throw exceptions; and
exception when ios_base::badbit
is set might be an exception (since it
represents a truly exceptional case of a hardward fault).
回答4:
Questions 1 and 2 are addressed at some length in this question.
Questions 3 and 5 are covered by the recommendation, in the accepted answer to that question, that you don't use exception specifications at all.
Question 4 seems to be adequately addressed by typing those exception names into the search engine of your choice, or consulting the index of a good C++ book. Do you have a specific query about them?