C++ Programming/Exception Handling

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit] Exception Handling

When designing a programming task (a class or even a function) one cannot always assume that application/task will run or be completed correctly (exit with the result it was intended to). It may be the case that it will be just inappropriate for that given task to report an error message (return an error code) or just exit. To handle these types of cases, C++ supports the use of language constructs to separate error handling and reporting code from ordinary code, that is constructs that can deal with these exceptions (errors and abnormalities) and so we call this global approach that adds uniformity to program design the exception handling.

An exception is said to be "thrown" at the place where some error or abnormal condition is detected. The throwing will cause the normal program flow to be aborted. Instead, execution of the program will resume at a designated block of code, called a "catch block", which encloses the point of throwing in terms of program execution. The catch block can be, and usually is, located in a different function/method than the point of throwing. In this way, C++ supports non-local error handling. Along with altering the program flow, throwing of an exception passes an object to the catch block. This object can provide data which is necessary for the handling code to decide in which way it should react on the exception.

Consider a code example for clarification:

void a_function()
{
  // This function does not return normally, 
  // instead execution will resume at a catch block.
  // The thrown object is in this case of the type char const*,
  // i.e. it is a C-style string. More usually, exception
  // objects are of class type.
  throw "This is an exception!"; 
}
void another_function()
{
  // To catch exceptions, you first have to introduce
  // a try block via " try { ... } ". Then multiple catch
  // blocks can follow the try block.
  // " try { ... } catch(type 1) { ... } catch(type 2) { ... }"
  try 
    {
      a_function();
      // Because the function throws an exception,
      // the rest of the code in this block will not
      // be executed
    }
  catch(char const* p_string) // This catch block 
                             // will react on exceptions
                             // of type char const*
  {
    // Execution will resume here.
    // You can handle the exception here.
  }
              // As can be seen
   catch(...) // The ellipsis indicates that this
              // block will catch exceptions of any type. 
   {
     // In this example, this block will not be executed,
     // because the preceding catch block is chosen to 
     // handle the exception.
   }
 }
try and catch block combination

[edit] Partial handling

Consider the following case:

 void g()
 {
   throw "Exception";
 }
 
 void f() {
   int* i = new int(0);
   g();
   delete i;
 }
 int main() {
   f();
  return 0;
 }

Can you see the problem in this code ? If g throws an exception, the variable i is never deleted and we have a memory leak.

To prevent the memory leak, f() must catch the exception, and delete i. But f() can't handle the exception, it doesn't know how!

What is the solution then? f() shall catch the exception, and then rethrow it:

 void g()
 {
   throw "Exception";
 }
 
 void f() {
   int* i = new int(0)
   try
   {
     g();
   }
   catch (...)
   {
     delete i;
     throw; // This empty throw rethrows the exception we caught
            // An empty throw can only exist in a catch block
   }
   delete i;
 }
 int main() {
   f();
   return 0;
 }

There's a better way though; see "Writing Exception-Safe Code" below for information on using "RAII" classes to avoid the need to write catch, which also explains why C++ can do better than "finally".

[edit] Exception hierarchy

You may throw as exception an object (like a class or string), a pointer (like char*), or a primitive (like int). So which should you choose? You should throw objects, as they ease the handling of exceptions for the programmer. It is common to create a class hierarchy of exception classes:

  • class MyApplicationException {};
    • class MathematicalException : public MyApplicationException {};
      • class DivisionByZeroException : public MathematicalException {};
    • class InvalidArgumentException : public MyApplicationException {};

An example:

float divide(float numerator, float denominator)
{
  if(denominator == 0.0)
    throw DivisionByZeroException();

}
enum MathOperators {DIVISION, PRODUCT};

float operate(int action, float argLeft, float argRight)
{ 
  if(action == DIVISION)
  {
    return divide(argLeft, argRight);
  }
  else if(action != PRODUCT))
  {
   // call the product function
   // ... 
  }
  // No match for the action! action is an invalid agument
  throw InvalidArgumentException(); 
}
 
int main(int argc, char* argv[]) {
  try
  {
     operate(atoi(argv[0]), atof(argv[1]), atof(argv[2]));
  } 
  catch(MathematicalException& )
  {
    // Handle Error
  }
  catch(MyApplicationException& )
  {
    // This will catch in InvalidArgumentException too.
    // Display help to the user, and explain about the arguments.
  }
  return 0;
}

NOTE:
The order of the catch blocks is important. A thrown object (say, InvalidArgumentException) can be caught in a catch block of one of its super-classes. (e.g. catch (MyApplicationException& ) will catch it too). This is why it is important to place the catch blocks of derived classes before the catch block of their super classes.

[edit] Throwing Objects

There are several ways to throw an exception object. Let's review them.

Throw a pointer to the object:

 void foo()
 {
   throw new MyApplicationException();
 }
 void bar()
 {
   try 
   {
     foo();
   }
   catch(MyApplicationException* e)
   {
     // Handle exception
   }
 }

But now, who is responsible to delete the exception? The handler? This makes code uglier. There must be a better way!

How about this:

 void foo()
 {
   throw MyApplicationException();
 }
 void bar()
 {
   try 
   {
     foo();
   }
   catch(MyApplicationException e)
   {
     // Handle exception
   }
 }

Looks better! But now, the catch handler that catches the exception, does it by value, meaning that a copy constructor is called. This can cause the program to crash if the exception caught was a bad_alloc caused by insufficient memory. In such a situation, seemingly safe code that is assumed to handle memory allocation problems results in the program crashing with a failure of the exception handler. The correct approach is:

 void foo()
 {
   throw MyApplicationException();
 }
 void bar()
 {
   try 
   {
     foo();
   }
   catch(MyApplicationException const& e)
   {
     // Handle exception
   }
 }

This method has all the advantages - the compiler is responsible for destroying the object, and no copying is done at catch time!

The conclusion is that exceptions should be thrown by value, and caught by (usually const) reference.

[edit] Stack unwinding

Consider the following code

void g()
{ 
 throw std::exception();
}

void f()
{
  std::string str = "Hello"; // This string is newly allocated
  g();
}

int main()
{
  try
  {
     f();
  } 
  catch(...) 
  { }
}

The flow of the program:

  • main() calls f()
  • f() creates a local variable named str
  • str constructor allocates a memory chunk to hold the string "Hello"
  • f() calls g()
  • g() throws an exception
  • f() does not catch the exception.
Because the exception was not caught, we now need to exit f() in a clean fashion.
At this point, all the destructors of local variables previous to the throw
are called - This is called 'stack unwinding'.
  • The destructor of str is called, which releases the memory occupied by it.
As you can see, the mechanism of 'stack unwinding' is essential to prevent resource leaks - without it, str would never be destroyed, and the memory it used would be lost forever.
  • main() catches the exception
  • The program continues.

The 'stack unwinding' guarantees destructors of local variables (stack variables) will be called when we leave its scope.

[edit] Writing exception safe code

[edit] Guards

If you plan to use exceptions in your code (and you should), you must always try to write your code in an exception safe manner. Let's see some of the problems that can occur:

Consider the following code:

 void g()
 { 
  throw std::exception();
 }
 
 void f()
 {
   int *i = new int(2);
   *i = 3;
   g();
   // Oops, if an exception is thrown, i is never deleted
   // and we have a memory leak
   delete i;
 }
 
 int main()
 {
   try
   {
      f();
   } 
   catch(...) 
   { }
   return 0;
 }

Can you see the problem in this code? When an exception is thrown, we will never run the line that deletes i!

What's the solution to this ? Earlier we saw a solution based on f() ability to catch and re-throw. But there is a neater solution using the 'stack unwinding' mechanism. But 'stack unwinding' only applies to destructors for objects, so how can we use it?

We can write a simple wrapper class:

 // Note: This type of class is best implemented using templates, discussed in the next chapter.
 class IntDeleter {
 public:
    IntDeleter(int* value)
    {
        m_value = value;
    }
 
    ~IntDeleter() 
     {
        delete m_value;
    }
 
    // operator *, enables us to dereference the object and use it
    // like a regular pointer.
    int&  operator *() 
    {
        return *m_value;
    }
 
 private:
     int *m_value;
 };

The new version of f():

 void f()
 {
   IntDeleter i(new int(2));
   *i = 3;
   g();
   // No need to delete i, this will be done in destruction.
   // This code is also exception safe.
 }

The pattern presented here is called a guard. A guard is very useful in other cases, and it can also help us make our code more exception safe. The guard pattern is similar to a finally block in other languages, like Java.

Note that the C++ Standard Library provides a templated guard by the name of auto_ptr.

[edit] Guide-lines

Because it is hard to write exception safe code, you should only use an exception when you have to - when an error has occurred which you can not handle. Do not use exceptions for the normal flow of the program. This example is WRONG.

 void sum(int a, int b) {
    throw a+b;
 }
 int main() {
    int result;
    try 
    {
      sum(2,3);
    }
    catch(int tmpResult)  
    {
      // Here the exception is used instead of a return value!
      // This is  wrong!
      result = tmpResult;
    }
   return 0;
 }

[edit] Exceptions in constructors and destructors

When an exception is thrown from a constructor, the object is not considered instantiated, and therefore its destructor will not be called.

What happens when we allocate this object with new ?

  • Memory for the object is allocated
  • The object's constructor throws an exception
    • The object was not instantiated due to the exception
  • The memory occupied by the object is deleted
  • The exception is propagated, until it is caught

The main purpose of throwing an exception from a constructor is to inform the program/user that the creation and initialization of the object did not finish correctly. This is a very clean way of providing this important information, as constructors do not return a separate value containing some error code (as an initialization function would).

In contrast, it is strongly recommended not to throw exceptions inside a destructor. It is important to note when a destructor is called:

  • as part of a normal deallocation (exit from a scope, delete)
  • as part of a stack unwinding that handles a previously thrown exception.

In the former case, throwing an exception inside a destructor can simply cause memory leaks due to incorrectly deallocated object. In the latter, the code must be more clever. If an exception was thrown as part of the stack unwinding caused by another exception, there is no way to choose which exception to handle first. This is interpreted as a failure of the exception handling mechanism and that causes the program to call the function terminate.

To address this problem, it is possible to test if the destructor was called as part of an exception handling process. To this end, one should use the standard library function uncaught_exception, which returns true if an exception has been thrown, but hasn't been caught yet. All code executed in such a situation must not throw another exception.

Situations where such careful coding is necessary are extremely rare. It is far safer and easier to debug if the code was written in such a way that destructors did not throw exceptions at all.