C++ Advanced Tutorial - Lesson 4
Related Blog Items
- C++ Tutorial Part 2 - Advanced
- C++ Basics - Tutorial
- C++ Advanced Tutorial - Lesson 11
- C++ Advanced Tutorial - Lesson 8
- C++ Advanced Tutorial - Lesson 3
Please refer Lesson 3..
4. EXCEPTION HANDLING
4.1 What is Exception Handling
4.2 Do Not Use Exception Handling on Normal Application Code
4.3 try Block
4.4 throw
4.5 Throw Point
4.6 catch Block
4.7 Rethrow an Exception
4.8 Exception Specifications
4.9 Stack Unwinding
4.10 Exception Thrown from Constructor and Destructor
4.11 Resource Leak
4.12 Insufficient Memory when Using new
4.13 Standard Library Exception Hierarchy
4.14 Difference Between C++ and Java’s Exception Handling
4. EXCEPTION HANDLING
4.1 What is Exception Handling
First consider a normal way to handle errors:
const float reciprocal( const float denominator ) { if(denominator == 0) { cout << "Divide by zero! \n"; // Exception handling code abort(); } // header file is <stdlib> return 1/denominator; }
This “reciprocal( )” method calculates the reciprocal of a given float number. The if block forms an exception handler.
The problems with such normal way of error handling are:
1)When the exception handling code becomes more complicated, the normal part of program becomes “polluted” with the exception handling code, and makes it more difficult and complicated to understand and maintain.
2)When you write a program you have to handle the errors yourself, or you should invoke a known class to handle it. There is no way that you can just detect the error, “publish” the error message, so that the client who makes use of your code can handle it in his own way.
The second reason is the major reason which calls for exception handling technique. There are cases when the place where the error happens is not the right place to handle it. Instead it is more appropriate to notice the caller of the method and let him handle it.
4.2 Do Not Use Exception Handling on Normal Application Code
There are the following reasons that exception handling shouldn’t be used on normal application code:
1)Exception handling is designed for error handling which very seldom happens. So the compiler is not required to optimize it well as it does on normal application code.
2)Flow of control with conventional control structures is generally clearer and more efficient than with exceptions.
3)When exceptions happens, the stack is unwound and resources allocated prior to the occurrence of the exception may not be freed. This problem can be avoided by careful programming.
4.3 try Block
A try block contains the code which may throw the exception, or contains the code that calls the method which may throw the exception.
4.4 throw
A throw keyword specifies an operand which can be of any type. The throw statement is actually passing information to the exception handler in the catch block. This information can be either only be the type of the thrown object - in many cases it is enough, or plus the extra information carried in the thrown object. The thrown object can be a class object or just a character string:
int main() { try { throw ("Testing string!"); } catch (char * s) { cout << s << endl; } }
4.5 Throw Point
The point where the exception is thrown is called throw point. When an exception is thrown, the control leaves the try block and searches for a catch block which can match this exception. If one catch block catches the exception, the code included will be executed, then the control will resume to the first statement after the last catch block. All codes between the throw point and the last catch block will be skipped. The control has no way to return to the throw point.
If the exception is not caught, the method call stack is unwound and control will further more exit to the outer scope and continue searching the following catch blocks. If it is already the most outer block such as main, method terminate is called, which by default will call method abort.
4.6 catch Block
=>Parameter Name in catch header
A catch keyword specifies in “( )” the type of the object to be caught, and an optional parameter name.
When exception happens, a temporary copy of the thrown object is created and initialized. If the catch block has defined a parameter name of that type, this name will be given to this temporary object and used to access the data members and methods of the object. If the catch block does not have an parameter name, then the thrown object’s internal information can not be accessed, and it is only used to match the catch block. After handler finishes, the temporary object is destroyed.
So if you want to pass addtional information from the thrown point to the catch block, you have to put this information inside the thrown object (as its data member) and define an object name in the catch block, so that you can access the information through it.
=>catch all
The following catch specification
catch(…)
will catch all exceptions.
=>catch through inheritance
Due to inheritance hierarchies, a derived-class object can be caught by a handler specifying either the derived-class type or the base-class type.
As a general rule, put the specific type handlers in the front, such as derived-class type handlers, and put the generic handlers after, such as base-class type handlers, and put “catch (… )” at the last. If you reverse the sequence, the exceptions will all be caught by generic handlers and will never get to the specific ones.
A “void *” handler will catch all exceptions of pointer type.
=>Release the Resource in catch Block
When an exception happens, the try block terminates and all automatic objects inside the try block are destroyed before the handler begins executing. However, it is possible that some allocated resources are not yet released in the try block. The catch handler, if possible, should release these resources, e.g., delete the space allocated by new, and close any files opened in the try block.
=>Copy Constructor of the Thrown Object
The handler must have access to the copy constructor of the thrown object. It means that the header file of the thrown object should be included.
4.7 Rethrow an Exception
A catch handler itself can discover an error and throw an exception. Or it may do some primary handling and then throw it again. To rethrow, simply say
throw;
A rethrown exception will exit the outer try block and search the following handlers.
“catch (… )” can be used to perform recovery that doesn’t depend on exception types, such as releasing common resources. Then it may rethrow the exception to alert more specific catch handlers.
4.8 Exception Specifications
An exception specification (also called throw list) is throw followed by a list of types enclosed in “( )”:
void print() throw(a, b, c) {...}
It can be placed after any method header, to restrict the kind of exceptions that can be thrown by this method. This is used to notice the client who make use of this code the types of possible exceptions your code may throw.
All derived-class types based on the throw list types can also be thrown.
If any exception not listed in the throw list is thrown, method unexpected is called, which will call the method specified by method set_unexpected, or by default it will call method terminate.
Method terminate will call the method specified by method set_terminate, or by default it will call method abort.
Header files of these methods
unexpected, set_unexpected:
terminate, set_terminate:
abort:
Method set_unexpected and set_terminate take method names i.e. function pointer as arguments. The methods must be with void return type and no arguments. Method set_unexpected and set_terminate returns a the name of the method last called by terminate or unexpected. This enables the programmer to save the name and use it later.
If the use-defined method specified in set_terminate or set_unexpected does not exit the program, method abort will be automatically called to end the program execution at last.
If you include exception “std::bad_exception” (derived from base class “exception” whose prototype is in header file
An empty throw list “throw” means that the method wouldn’t throw any exception without alerting method unexpected.
4.9 Stack Unwinding
When an exception is thrown but not caught in a certain scope, the method call stack is unwound , which means that the method which throwns the exception terminates, all local objects destroyed, then control returns to the point where this method was called.
If that calling point is in a try block, control will leave that try block and search the following handlers. If that point is not in a try block or the search fails, stack unwinding will happen again — until it reaches main, and method terminate will be called, as said before.
#include <iostream> #include <stdexcept> using namespace std; void method3() throw(runtime_error) { throw runtime_error( "Thrown by method 3!" ); cout << "In method3: shouldn''t appear! \n"; } void method2() throw(runtime_error) { method3(); cout << "In method2: shouldn't appear !\n"; } void method1() throw(runtime_error) { method2(); cout << "In method1: shouldn't appear !\n"; } int main() { try { method1(); } catch ( runtime_error e ) { cout << "Exception caught: " << e.what() << endl; } cout << "In main: should appear! \n"; int a; cin >> a; }
As a general result, if you don’t provide any exception handling code and exception happens, the program will be terminated. But if you provide a proper exception handling measure, the program can go on working.
4.10 Exception Thrown from Constructor and Destructor
When an exception is thrown in a constructor, destructors for all the local objects created as part of the object being constructed will be called. If a destructor which is called to unwind the stack throws out an exception, method terminate will be called.
Normally you should avoid throwing an exception out of a constructor without catching it and doing necessary clean up in the constructor first. A simple solution is to use default constructor to create a very “safe” model of object, then use another method which is typically called “Init” in many applications to initialize it.
The following example demonstrates why you should avoid throwing exception out of constructor without catching and processing it first. Suppose in the constructor before the throw point you have allocated some dynamic memory with operator new to a pointer data member:
class Employee { ... Image * const m_pImage; AudioClip * const m_pAudio; }; Employee(..., string image, string audio) : ..., m_pImage(NULL), m_pAudio(NULL) { ... m_pImage = new Image(image); m_pAudio = new AudioClip(audio); // throw point }
When the exception is thrown by “new AudioClip(audio)”, the constructor is exited, the dynamic memory become memory leak. The destructor of the class is not called, because a destructor is only called for a fully constructed object, and because the constructor is half-way termicated by the throwing of exception, the object is not fully constructed.
The correct way to deal with such situation is to catch possible exception in the constructor, and in the catch block, delete all dynamic allocated resources, then rethrow the exception again as a notification:
Employee(..., string image, string audio) : ..., m_pImage(NULL), m_pAudio(NULL) { ... try { m_pImage = new Image(image); m_pAudio = new AudioClip(audio); } catch(...) { delete m_pImage; delete m_pAudio; throw; } }
This solution works fine except if the class uses constant pointer members:
class Employee { ... Image * const m_pImage; AudioClip * const m_pAudio; };
Such pointer members must be initialized in the member initializer, where you can not put try and catch blocks.
The ultimate solution is: use auto_ptr< > class or similar classes to wrap the raw pointers, so that they become local objects:
class Employee { ... auto_ptr<Image> m_image; auto_ptr<AudioClip> m_audio; };
As we already know, when a constructor is terminated by exception, all local objects are destroyed by calling their destructors. The destructor of auto_ptr< > will delete the object pointed by the wrapped pointer.
4.11 Resource Leak
If a resource is allocated before the throw point and supposed to be released after the throw point, when exception happens it prevents the resource from being released. This is called resource leak. If the resource is memory, it is called memory leak.
One technique to resolve this problem is to create a local object with a pointer which points to this resource, and define a proper destructor for this object to delete the resource. Then when exception happens, destructors for this object will be called, and the resource will be released.
C++ has already provided a class template, auto_ptr, which contains a pointer pointing to a given type. The destructor of this class destroys the resource to which the pointer points to. Operator “->” and “*” have been overloaded, so that auto_ptr objects can be used as normal pointers to access and dereference. For example,
auto_ptr<int> ptr = new Employee("Frank Liu");
ptr will be treated as a local object and its destructor will be automatically called when leaving scope, in which there is usually the following statement
delete ptr;
Header file of auto_ptr is
4.12 Insufficient Memory when Using new
When new fails to obtain memory, older-version compilers will return 0. The ANSI/ISO C++ draft standard version specifies that when new fails, it automatically throws out a “std::bad_alloc” exception defined in header file
int * ptr = new (nothrow) int[500];
If you pass the name of a user-defined function (which has void return type and no arguments) as an argument to method “set_new_handler”, whose header file is
4.13 Standard Library Exception Hierarchy
Experience shows that exceptions fall nicely into a number of categories. The C++ draft standard includes a hierarchy of exception classes. The base class is “exception”, whose header file is
Exception class “runtime_error” and “logic_error” are immediately derived from “exception”. Their header file is
From “logic_error” there are:
invalid_argument invalid arguments are passed to a method
length_error a length larger than the maximum size of the object is used to that object.
out_of_range a value is out of range, such as a subscript to an array.
From “runtime_error” there are:
overflow_error
underflow_error
Also derived from class “exception” are the exceptions thrown by C++ language features:
bad_alloc thrown by new when it fails to obtain memory
bad_typeid
bad_cast thrown by dynamic_cast.
4.14 Difference Between C++ and Java’s Exception Handling
The biggest difference is: Java compiler forces that all exceptions should be caught and handled.
Popularity: 24%
You need to log on to convert this article into PDF
Related Blog Items - C++ Tutorial Part 2 - Advanced
- C++ Basics - Tutorial
- C++ Advanced Tutorial - Lesson 11
- C++ Advanced Tutorial - Lesson 8
- C++ Advanced Tutorial - Lesson 3
Related Blog Items
- C++ Tutorial Part 2 - Advanced
- C++ Basics - Tutorial
- C++ Advanced Tutorial - Lesson 11
- C++ Advanced Tutorial - Lesson 8
- C++ Advanced Tutorial - Lesson 3
Some of the header files are not mentioned in the text. You have “header file is .” without stating the header file. I viewed source on the page and noticed that you had the header file like which the browser obviously mistook for a html element and hence wasn’t displayed on the page. Just thought you might have wanted to know as I am sure this is just a silly mistake that was overlooked. BTW, Great Tutorial! I know a good bit about C++ and I still learned a lot. Thanks!
February 6th, 2008 at 4:47 am
Oops, it seems that my comment was treated as html and as such didn’t display part of it. I meant that I viewed source on the page and noticed that you had the header file like which the browser obviously mistook for a html element and hence wasn’t displayed on the page
February 6th, 2008 at 4:51 am
Dang it! I accidently hit the Post Comment button. Ok, for the third time, what I meant was:
“I viewed source on the page and noticed that you had the header file like ” (without the ‘) which the browser obviously mistook for a html element and hence wasn’t displayed on the page”
Sorry about that confusion.
February 6th, 2008 at 4:53 am
It still didn’t work. Just view source and you will see what I am talking about.
February 6th, 2008 at 4:53 am