- traditional error handling:
- return value or set global error variable.
- signal() & raise()
- setjmp() & longjmp()
C++ standard states: branching into a scope with goto, or branching out of a scope with longjmp() where an object on the stack has a destructor, constitutes undefined behavior.
- you can use any type when you throw(including built-in types).
- Stack unwinding : any local objects(exclude objects allocated on heap) created by the time the exception occurs are destroyed automatically.
- polymorphism works for exceptions and you should catch by reference to avoid "object-slicing"
- an object or reference to a derived-class object will match a handler for the base class.
no automatic type conversions are used to convert one exception type to another in the process of matching
- it make more sense to catch the derived types first and put the base type at the end to catch anything less specific
- catch(...) means catching any type of exception
- you can rethrow the exception by using throw with no argument inside a handler
- terminate() is automatically called
- if no handler at any level catches the exception
- if a destructor for a local objects throws an exception while stack is unwinding
- if a global or static object's constructor or destructor throws an exception
- DO NOT allow a destructor to throw an exception
- use set_terminate(func) to customize your terminate()
func() must take no arguments and return void, and can NOT throw any exception
- C++ exception handing guarantees that as you leave a scope, all objects in that scope whose constructors have been completed will have their destructor called.(it should be your design principle, the compiler doesn't do it ???)
- constructors that aren't completed don't have the associated destructor called
- Resource management
- if you allocate resources in constructors and the constructor throws an exception, then the destructror doesn't get a chance to deallocate the resource.
- to avoid resource leaks, you can:
- catch exceptions inside the constructor and release the resource
- make everything an object -- Resource Acquisition Is Initialization (RAII)
- auto_ptr: an handy tool
- function level try blocks
- standard exceptions
- “void f()” means that any type of exception can be thrown
- “void f() throw()” means that no exceptions whatsoever will be thrown
- unexpected() is called when you throw something other than what appears in the exception specification
- you can use your own unexpected() by using set_unexpected(), your custom function should have no arguments and return void
- a typical unexpected handler logs the error and terminates the program by calling exit(), however, it can throw another exception(or rethrow the same exception) or call abort()
- if unexpected handler rethrow the same exception:
- if std::bad_exception was in the function’s exception specification, the exception throw from the unexpected handler is replaced with a std::bad_exception object, and the search resumes from the function as before.
- if the original function’s specification did not include std::bad_exception, terminate() is called
- if a member function in a base class says it will only throw an exception of type A, an override of that function in a derived class must not add any other exception types to the specification list, it can specify fewer exceptions or none or anything that “is-a” A in place of A
- if you don’t know what exceptions might occur, don’t use exceptionn specifications
Exception specifications are mainly for non-template classes
Exception Safety: you should know why pop() in STL stack container doesn’t return a value.
Programming with Exceptions:
When to avoid exception:
- Best advice: throw exceptions only when a function fails to meet its specification
- Not for asynchronous events
- Not for benign error conditions
if you have enough information to handle an error, solve the problem in the current context rather than throwing an exception to a larger context
- Not for flow of control
- You’re not forced to use exceptions
Typical uses of exceptions
- fix the problem and retry the function that caused the exception
- patch things up and continue without retrying the function
- do whatever you can in the current context and rethrow the same exception to a higher context
- do whatever you can in the current context and throw a different exception to a higher context
- terminate the program
- wrap functions that use ordinary error schemes so they produce exceptions instead
- simplify your error handling scheme
- make your library and program safer
- anytime you use exception specifications, consider creating your own unexpected() function
- start with standard exceptions
- nested your own exceptions
- use exception hierarchies
- catch by reference, not by value
- throw exception in constructor
- DO NOT cause exceptions in destructor
- avoid naked pointers