- The Importance of Handling Exceptions
- A C++ Exception with No Handler
- A C++ Exception in a Call from Python
- Conclusion
- References
A C++ Exception with No Handler
Now say we want to see what happens if a C++ program throws an exception during its invocation. To demonstrate this possibility in Listing 8, let's reuse the C++ code from Listing 6. To make it a little more interesting (well, I think it's interesting, anyway <g>), I'll refactor the C++ code as the function callADynamicAllocation().
Listing 8A C++ exception handler in action.
void callADynamicAllocation() { int* myarray = NULL; if(myarray){ cout << "Not NULL" << endl; }else{ cout << "Is NULL" << endl; } try { int *myarray = new int[1000000000]; std::cout << "Calling typeid: " << typeid (myarray).name() << std::endl; } catch(exception & e){ cout << "Standard exception: " << e.what() << endl; if(myarray){ cout << "Not NULL" << endl; }else{ cout << "Is NULL" << endl; } } if(myarray){ cout << "Deleting myarray" << endl; delete [] myarray; } cout << "Returning" << endl; } int main () { callADynamicAllocation(); return 0; }
Just to show I'm not cheating, Listing 9 shows the output from the refactored C++ program.
Listing 9A C++ exception handler in action—same output as in Listing 7.
Is NULL Standard exception: std::bad_alloc Is NULL Returning
No surprises in running the C++ code—it catches the exception. To make it more fun, let's add a new function with the same code, but without the exception handler code, producing Listing 10.
Listing 10A C++ exception handler in action, and the same code with no handler.
void callADynamicAllocation() { int* myarray = NULL; if(myarray){ cout << "Not NULL" << endl; }else{ cout << "Is NULL" << endl; } try { int *myarray = new int[1000000000]; std::cout << "Calling typeid: " << typeid (myarray).name() << std::endl; } catch(exception & e){ cout << "Standard exception: " << e.what() << endl; if(myarray){ cout << "Not NULL" << endl; }else{ cout << "Is NULL" << endl; } } if(myarray){ cout << "Deleting myarray" << endl; delete [] myarray; } cout << "Returning" << endl; } void callADynamicAllocationNoExceptionHandler() { int *myarray = new int[1000000000]; std::cout << "Calling typeid: " << typeid (myarray).name() << std::endl; if(myarray) { cout << "Deleting myarray" << endl; delete [] myarray; } cout << "Returning" << endl; } int main () { callADynamicAllocationNoExceptionHandler(); return 0; }
When I run the code in Listing 10, here's what happens:
terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc
Notice in Listing 10 that we get unceremoniously dumped out of the program during the call to callADynamicAllocationNoExceptionHandler(). The runtime system detects the exception and reports it, but our code gets no opportunity at all to clean up, which could leave things in a nasty state; for example, with files open and/or partially updated, memory allocated, and so on. That's why it's a good idea to implement an exception handler.
Leaving out exception management in this case is a kind of anti-pattern. In this case, the separation of concerns we've used implicitly leaves exception handling up to the runtime system—the "higher calling code." It's the latter that must try to recover from the exception.