Error handling.

 
If anything can possibly go wrong, it will.

Handling exceptional situations

There are several techniques that allow programmers handle exceptional situations. These are some ways to handle run time errors:

Exceptions. Standard exception hierarchy

The last way the exception mechanism, was added to C++ in order to replace the earlier techniques dealing with errors. The key idea of the new mechanism is an idea of throwing an exception. When a function (or a code) detects an error, it signals about the conditions by creating and throwing an exception. For example,
int& Array::Item(int index)
{
   if( index<0 || index>=size ){
      out_of_range err("index is out of range in Array::Item(int)");
      throw err;
   }
   return data[index];
} 
where the out_of_range is a standard exception class declared in the <stdexcept>. The new command throw terminates the function and throws the exception. However, unlike all other methods, the function does not return to its caller. Instead, it searches for a handler that specifies how to deal with out_of_range exceptions. The search is going in the caller, caller's caller, and so forth. When a handler is found, the associated code (in the corresponding catch) clause is executed.

The try and catch construct

C++ uses the following syntax to specify exception handlers for some portion of the program's code:
try{
   statements; // code for with we want to sat the handlers
}
catch( type_1 value_1 ){
   statements; // code for handling exceptions type_1
}
[ catch( type_2 value_2 ){
   statements; // code for handling exceptions type_2
} ]
...
[ catch( type_n value_n ){
   statements; // code for handling exceptions type_n
} ]
For example, we can use the following code to detect out of range exceptions:
Array a;
try{
   cout<<"count = "<<a.Count()<<endl;
   cout<<" size = "<<a.Size()<<endl;
   a.Item(10) = 5;
   cout<<"a.Item(10) = "<<a.Item(10) = 5;
}
catch(out_of_range& e){
   cerr<<"The following error has been detected:\n\t"<<e.what()<<endl;
}
This code will execute correctly down to the line
a.Item(10) = 5;
method Item() will throw an exception because the value 10 is outside of the allowed index values (0 - 9). Thus, method Item() will be immediately terminated and the program will locate the handler for the exceptions of the out_of_range type. A handler for exactly this type of exception is specified in the catch clause; that is, the line
cerr<<"The following error has been detected:\n\t"<<e.what()<<endl;
will be executed.

Technically speaking, we can throw any type of value, not necessarily an object of the exception class or any derived class. We can even throw a value of a primitive type like int or bool:

try{
  cout<<"Throwing an integer value (7) ...\n";
  throw 7;
  cout<<"You shouldn't see this line";
}
catch(int i){
  cout<<"Value "<<i<<" cought\n";
}
However, these kind of values do not contain enough information about the error happened. For this reason, it's traditional to throw objects that are either derived from class exception (directly or indirectly) or programmer created classes designed for the same purpose. The following diagram contains the hierarchy of standard exception types in C++.
All standard exception classes have a constructor that takes an argument of the string type (usually programmers pass a description of the error occurred) and method what() that returns the string passed to the constructor. If you need to create your own classes for exception situations we suggest you derive your classes from the exception class or one of the derived classes. For example, we can create a class that derives from the logic_error class:
class CustomDefinedException : public logic_error {
  public:
     CustomDefinedException(const string &msg) : logic_error(msg) {};
};
Now you can throw the newly created exception:
if( gpa < 1.2 )
   throw CustomDefinedException("GPA is too low");
Please note that this exception can be caught by
catch( CustomDefinedException &e ){ cerr<<"Error: "<<e.what; }
or as logic_error exception by
catch( logic_error &e ){ cerr<<"Error: "<<e.what; }
or even as exception
catch( exception &e ){ cerr<<"Error: "<<e.what; }

We also can catch different types of exceptions by putting several catch clauses. For instance, the following example catches user defined exceptions, logic error exceptions, and any other exception:

try{
   statements;
}
catch( CustomDefinedException &e ){ 
   cerr<<"Error: "<<e.what; 
}
catch( logic_error &e ){ 
   cerr<<"Error: "<<e.what; 
}
catch( exception &e ){ 
   cerr<<"Error: "<<e.what; 
}
Please note that the order of these catch clauses is important. In the code above if an exception thrown, it's first tested on being a CustomDefinedException exception, if it's not, then it's tested on being logic_error exception, etc. If we put the logic_error catch first, then any custom defined exception will be identified as logic_error exception because it's derived from the logic_error class.

You don't need to have catch clauses for all possible types of exceptions, if an appropriate handler is not found, the program keeps searching for a handler in the caller, etc. However, if you want to catch all possible exceptions no matter if they are derived from exception class or not, you can use special catch(...) clause:

try{
   statements;
}
catch( CustomDefinedException &e ){ 
   cerr<<"Error: "<<e.what; 
}
catch( logic_error &e ){ 
   cerr<<"Error: "<<e.what; 
}
catch(...){  
   cerr<<"Unknow exception caught."; 
}

The throw command can be used without any argument inside a catch clause. In this case, this command re-throws the same exception to the caller's exception handler. The following example, shows how to re-throw an exception from a function to the function's caller:

int rethrow_exception()
{
   try{
      cout<<"Function rethrow_exception() is invoked\n";
	  throw domain_error("just throw any excepiton");
   }
   catch(exception &e){
      cerr<<"Something went wrong: "<<e.what();
      throw; // rethrow the exception
   }
}  

void main()
{
   try{
      rethrow_exception();
   }
   catch(domain_error &e){
      cerr<<"Caught an exception in the main() function: "<<e.what();
   }
}

Exceptions in constructors and destructors

We should be careful when using throwing exceptions in constructors. For example, the following constructor is not good enough:
HashArray::HashArray(int size)
{
   try{
      keys = new string[size];
      values = new int[size];
   }
   catch( bad_allocation &e ){
      cerr<<"Cannot allocate enough memory in HashArray("<<size<<")";
      throw;
   }
}
Why? To answer the question let's think what happens if we successfully executed the first new operator, but not the second one. Obviously, an exception will be thrown, caught inside the constructor, and then re-thrown to report about the problem. However, since the constructor was terminated and not successfully finished, the object is not yet created. That means, that the destructor deallocating memory will not be invoked. Thus, the memory, allocated for the private keys stays allocated. In order to fix the constructor, we need to deallocate the memory inside the constructor's catch:
HashArray::HashArray(int size) : keys(NULL), values(NULL)
{
   try{
      keys = new string[size];
      values = new int[size];
   }
   catch( bad_allocation &e ){
      delete[] keys;
      delete[] values;
      throw;
   }
}

We also need to be careful throwing exceptions inside destructors. Many destructors are invoked when the main() function is over. That means, that they are not inside any try clause. That is, there would be no appropriate catch for them.

Microsoft extension: the __finally block

Microsoft© version of C++ provides the __finally clause to usual try blocks. Thus, in MS Visual C++ the complete syntax of the try statement looks like
try{
   statements; // code for with we want to sat the handlers
}
catch( type_1 value_1 ){
   statements; // code for handling exceptions type_1
}
[ catch( type_2 value_2 ){
   statements; // code for handling exceptions type_2
} ]
...
[ catch( type_n value_n ){
   statements; // code for handling exceptions type_n
} ]
[ __finally{
   statements; // this code will be executed anyways 
} ]
Statements in the __finally clause will be executed no matter was an exception thrown or not. In fact, this code will be executed even an exception was thrown and not caught. If everything in the try clause goes fine, then the code in the __finally block is executed immediately after the last statement in the try block. If something goes wrong, and an exception occurs, then program searches for an appropriate exception handler and if it's found, then the corresponding catch statements are executed, and then the __finally block; if a handler not found, then the __finally block is run, and only then the program continues its search for an exception handler. Please study the example program on the example page.

This property of the __finally block makes it a perfect place to release resources allocated in the corresponding try block. Please note that programs containing the __finally block should be compiled with the /clr key (compile for the common language runtime).

Mircosoft also provides its own exception hierarchy. The new base class Exception is defined and all other exception classes are derived from this base class. In this document we provide a list of MS exception classes. For complete information please consult MSDN library.

References