Flylib.com

Books Software

 
 
 

FAQ 9.03 What happens to objects in stack frames that become unwound during the throw catch process?

FAQ 9.03 What happens to objects in stack frames that become unwound during the throw / catch process?

graphics/new_icon.gif

They are properly destructed.

Local objects that reside on the stack between the throw and the catch are properly destructed in stack order; last constructed , first destructed. The result is an extension of the C++ destructor discipline, and allocated resources can be safely kept in an object whose destructor releases the resource. This resource is often memory, but it could also be files that need to be closed, semaphores that need to be unlocked, and so on. For example, a local auto_ptr should be used to hold the pointer to an allocated object, as shown in FAQ 2.07.

FAQ 9.04 What is an exception specification?

graphics/new_icon.gif

A specification indicating which exception objects a function expects to throw.

For example, in FAQ 2.23, routine fileExists() is decorated with the specification throw() , indicating that fileExists() never throws any exceptions, and routine processFile() is decorated with the specification throw(BadfileName, FileNotFound) , indicating that processFile() expects to throw BadFileName or FileNotFound (or some object derived from those classes) but nothing else.

If a function throws an exception other than those listed in the exception specification, the unexpected() function is called, which (by default) calls terminate() , which (by default) calls abort() . See FAQ 26.11 for how to change this default behavior.

In general, exception specifications should be used. One place where they are contraindicated, however, is where bug fixes for very large systems are shipped to customers in small binary pieces that are "patched" into the original binary. This is because exception specifications can unnecessarily increase the number of source files that must be shipped with such a bug fix. However for those systems that ship bug fixes as a complete executable, exception specifications should be used.

FAQ 9.05 What are the disadvantages of using return codes for error handling?

They don't separate exceptional logic from normal logic as well as exceptions do, they impose avoidable overhead, and they can't be used in constructors.

Return codes are a nice-guy approach; they allow the caller to do something when an error occurs but they don't require the caller to do anything or even notice that the error has occurred.

Return codes require an explicit if -check after every function call. This spreads the error-handling code into every caller of every function rather than focusing it on the relatively few routines that can actually correct the problem. Return codes therefore create a complex chain that is hard to test and maintain—everyone percolates the error information backward until finally someone is capable of handling it.

Since testing for return codes requires a conditional branch in the normal execution path , it imposes runtime costs for situations that rarely occur. When functions were hundreds of lines long, checking for return codes was a small percentage of the executable code. But with OO, where member functions often have less than ten lines of code, return codes would impose an unnecessary performance penalty.

Return codes can't be returned from constructors. Fortunately constructors can (and should) throw exceptions. So using return codes with constructors can be disastrous since return codes allow errors to remain uncorrected. For example, if a hash table can't allocate memory for its hash buckets, it might set an error flag within its object, hoping the caller will check this flag and do the right thing. Thus all the object's callers are expected to check this flag (presumably another member function that would have to be added), and all the object's member functions would also have to check the flag. This adds a lot of unnecessary decision logic as well as overhead.