Professional C__ - Marc Gregoire [162]
Although it’s important to know about set_terminate(), it’s not a very effective exception-handling approach. We recommend trying to catch and handle each exception individually in order to provide more precise error handling.
Throw Lists
C++ allows you to specify the exceptions a function or method intends to throw. This specification is called the throw list or the exception specification. Here is the readIntegerFile() function from the earlier example with the proper throw list:
void readIntegerFile(const string& fileName, vector throw(invalid_argument, runtime_error) { // Remainder of the function is the same as before } Code snippet from ReadIntegerFile\ThrowList.cpp The throw list shows the types of exceptions that can be thrown from the function. Note that the throw list must also be provided for the function prototype: void readIntegerFile(const string& fileName, vector throw(invalid_argument, runtime_error); Code snippet from ReadIntegerFile\ThrowList.cpp You cannot overload a function based solely on different exceptions in the throw list. If a function or method specifies no throw list, it can throw any exception. You’ve already seen this behavior in the previous implementation of the readIntegerFile() function. If you want to specify that a function or method throws no exceptions, you need to use noexcept like this: void readIntegerFile(const string& fileName, vector Code snippet from ReadIntegerFile\noexcept.cpp The noexcept keyword is introduced with C++11. If your compiler is not yet C++11 compliant, you should replace the noexcept keyword with throw(). However, it is important to remember that C++11 has deprecated the empty throw() list, which means that it might disappear in future compilers. If this behavior seems backward to you, you’re not alone. However, it’s best just to accept it and move on. A function without a throw list can throw exceptions of any type. A function with noexcept or an empty throw list shouldn’t throw any exception. Unexpected Exceptions Unfortunately, the throw list is not enforced at compile time in C++. Code that calls readIntegerFile() does not need to catch the exceptions listed in the throw list. This behavior is different from that in other languages, such as Java, which requires a function or method to catch exceptions or declare them in their own function or method throw lists. Additionally, you could implement readIntegerFile() like this: void readIntegerFile(const string& fileName, vector throw(invalid_argument, runtime_error) { throw 5; } Code snippet from ReadIntegerFile\UnexpectedExceptions.cpp Even though the throw list states that readIntegerFile() doesn’t throw an int, this code, which obviously throws an int, compiles and runs. However, it won’t do what you want. Suppose that you write this main() function which has a catch block for int: int main() { vector const string fileName = "IntegerFile.txt"; try { readIntegerFile(fileName, myInts); } catch (int x) { cerr << "Caught int" << endl; } return 0; } Code snippet from ReadIntegerFile\UnexpectedExceptions.cpp When this program runs and readIntegerFile() throws the int exception, the program terminates. It does not allow main() to catch the int. Throw lists don’t prevent functions from throwing unlisted exception types, but they prevent the exception from leaving the function, resulting in a run-time error. All versions of Microsoft Visual C++, up to version 2010 at the time of this writing, do not yet support throw lists for functions as explained earlier. As a result, the preceding main() function will catch the int exception because Visual C++ just ignores the throw(invalid_argument, runtime_error) specification and will issue a warning like “warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)”. Maybe future versions will support this. You can change this behavior. When a function throws an exception that is not