Professional C__ - Marc Gregoire [161]
try {
readIntegerFile(fileName, myInts);
} catch (...) {
cerr << "Error reading or opening file " << fileName << endl;
return 1;
}
Code snippet from ReadIntegerFile\MatchingAnyException.cpp
The three dots are not a typo. They are a wildcard that match any exception type. When you are calling poorly documented code, this technique can be useful to ensure that you catch all possible exceptions. However, in situations where you have complete information about the set of thrown exceptions, this technique is considered suboptimal because it handles every exception type identically. It’s better to match exception types explicitly and take appropriate, targeted action.
A possible use of a catch block matching any exception is as a default catch handler. When an exception is thrown, a catch handler is looked up in the order that they appear in the code. The following example shows how you can write catch handlers that explicitly handle invalid_argument and runtime_error exceptions and how to include a default catch handler for all other exceptions.
try {
// Code that can throw exceptions
} catch (const invalid_argument& e) {
// Handle invalid_argument exception
} catch (const runtime_error& e) {
// Handle runtime_error exception
} catch (...) {
// Handle all other exceptions
}
Uncaught Exceptions
If your program throws an exception that is not caught anywhere, the program will terminate. Basically there is a try/catch construct around the call to your main() function which catches all unhandled exceptions, something as follows:
try {
main(argc, argv);
} catch (...) {
// issue error message and terminate program
}
However, this behavior is not usually what you want. The point of exceptions is to give your program a chance to handle and correct undesirable or unexpected situations.
Catch and handle all possible exceptions thrown in your programs.
Even if you can’t handle a particular exception, you should still write code to catch it and print or show an appropriate error message.
It is also possible to change the behavior of your program if there is an uncaught exception. When the program encounters an uncaught exception, it calls the built-in terminate() function, which calls abort() from try { main(argc, argv); } catch (...) { if (terminate_handler != nullptr) { terminate_handler(); abort(); } else { terminate(); } } // normal termination code Before you get too excited about this feature, you should know that your callback function must still terminate the program, or else abort() will be called anyway. It can’t just ignore the error. However, you can use it to print a helpful error message before exiting. Here is an example of a main() function that doesn’t catch the exceptions thrown by readIntegerFile(). Instead, it sets the terminate_handler to a callback that prints an error message before exiting: void myTerminate() { cout << "Uncaught exception!" << endl; exit(1); } int main() { vector const string fileName = "IntegerFile.txt"; set_terminate(myTerminate); readIntegerFile(fileName, myInts); for (size_t i = 0; i < myInts.size(); i++) { cout << myInts[i] << " "; } cout << endl; return 0; } Code snippet from ReadIntegerFile\TerminateHandler.cpp Although not shown in this example, set_terminate() returns the old terminate_handler when it sets the new one. The terminate_handler applies program-wide, so it’s considered good style to reset the old terminate_handler when you have completed the code that needed the new terminate_handler. In this case, the entire program needs the new terminate_handler, so there’s no point in resetting