< C++ .NET Exception Handling 2 | Main | C++ .NET Exception Handling 4 >


 

 

Exception Handling 3

 

 

  1. Nesting and Re-throwing Exceptions

  2. The finally Block

 

 

Nesting and Re-throwing Exceptions

 

Now that you’ve seen how to use the try and catch construct, let’s move on to cover some more advanced uses. The first of these are nesting and re-throwing exceptions. As the name implies, nesting exceptions means including one try and catch construct inside another, which can provide a useful way to handle error conditions. It works as you might expect:

try   // outer try block

{

    try // inner try block

    {

        // Do something

    }

    catch(SomeException^ pex)

    {

        Console::WriteLine(L"Exception: {0}", pex->Message);

    }

}

catch(OtherException^ pex)

{

    Console::WriteLine(L"Exception: {0}", pex->Message);

}

If an exception occurs within the inner try block that is of type SomeException^, it will be handled by the inner catch block and execution will continue after the end of the inner catch block as usual. The outer catch block will not be executed in this case because the error has already been adequately handled. If an exception occurs within the inner try block that is of type OtherException^, it won’t be handled by the inner catch block, so it will be passed to the outer try and catch construct, where it is processed by the outer catch block. You can nest try and catch constructs to several levels, but it’s unusual to go more than two levels deep because it can complicate the structure of the code. Re-throwing an exception means just that, handling an exception in a catch block and then throwing it again so that it can be handled somewhere else. The following exercise shows how to catch an exception and re-throw it.

 

1.        Start Visual C++/Studio .NET and create a new CLR Console Application project named Rethrow.

 

Creating a new CLR Console Application project named Rethrow

 

2.        Immediately after the using namespace System; line and immediately before main(), add the following function definition:

void func(int a)

{

    try

    {

        if (a <= 0)

           throw gcnew System::ArgumentException(L"Aaargh! What wrong?");

    }

    catch(System::ArgumentException^ pex)

    {

        Console::WriteLine(L"Exception caught in func()");

    }

}

 

Exception handling - Adding a function that contain the try-catch-throw the exception

 

This function is basically the same simple function you met at the start of the module, which throws a System::ArgumentException when it has passed a negative argument. The difference here is that the exception is being caught within the function.

 

3.        Modify the main() function so that it looks something like this:

int main(array<System::String ^> ^args)

{

      Console::WriteLine(L"Throw Test");

      try

      {

          int n = 0;

          Console::WriteLine(L"Calling with n = 0");

          func(n);

      }

      catch(System::ArgumentException^ pex)

      {

          Console::WriteLine(L"Exception caught in main()");

      }

      Console::WriteLine(L"All done");

    return 0;

}

  Exception handling - Adding a try-catch block inside the main program

 

4.        If you run this code, you’ll find that the exception is caught locally in func and the catch block in main() doesn’t execute.

 

Exception handling - Catching the exception within the main program block or locally

 

5.        Modify the definition of func so that it re-throws the exception after handling it.

void func(int a)

{

    try

    {

      if (a <= 0)

        throw gcnew System::ArgumentException(L"Aaaraaghhh! What wrong?");

    }

    catch(System::ArgumentException^ pex)

    {

        Console::WriteLine(L"Exception caught in func()");

        throw;   // re-throw the exception

    }

}

 

Using throw without an argument re-throws the current exception, and it can be used in this way only within a catch block. At this point, the runtime goes off looking for another handler, which means moving up the call stack to the main() function, where the exception gets caught a second time. If you run this code, you should see the “Exception caught in func()” and “Exception caught in main()” messages printed.

 

Exception handling - Exception caught in bothe the main program and in the function

 

Note that you don’t have to re-throw the same exception; it’s quite usual to catch one type of exception, handle it, and then re-throw an exception of another type. You’ll see an example of this in the section on creating your own exception types later in this chapter.

 

The finally Block

 

Managed C++ adds a new construct to traditional C++ exception handling, the finally (the old version is __finally) block. The purpose of this block is to let you clean up after an exception has occurred, and the following short exercise shows how it works. Note that the finally block is always executed, even if no exception was thrown. The catch block is only executed if a managed exception is thrown within the associated try block.

 

1.        Reopen the project from the previous example if you closed it.

2.        Modify the main() function so that it looks like this, adding a finally block after the catch block:

Console::WriteLine(L"Throw Test");

try

{

   int n = 3;

   Console::WriteLine(L"Calling with n = 3");

   func(n);

   Console::WriteLine(L"Calling with n = 0");

   n = 0;

   func(n);

}

catch(System::ArgumentException^ pex)

{

   Console::WriteLine(L"Exception was {0}", pex);

}

finally

{

   Console::WriteLine(L"This is the finally block");

}

Console::WriteLine(L"All done");

3.        If you try executing the code, you’ll find that the finally block gets executed after the catch block.

 

Exception handling - the finally block gets executed after the catch block

 

4.   Modify the main() function so that the second call doesn’t cause an exception, either by changing the value or by commenting it out (changing it to n = 4 in this example). When you run the program again, you’ll see that the finally block still gets executed, even though there was no error.

 

Exception handling - the finally block still gets executed, even though there was no error

 

The purpose of this block is to ensure that if you do something in the try block, such as opening a file or allocating some memory, you’ll be able to tidy up whether an exception occurs or not because the finally block is always executed when execution leaves a try block. This construct gives you a way to clean up what might otherwise require complex coding. The following is another very simple example to describe how to using finally.

// keyword finally program example

// compile with: /clr

#include "stdafx.h"

using namespace System;

 

ref class MyException: public System::Exception{};

 

void ThrowMyException()

{

   throw gcnew MyException;

}

 

int main()

{

   try {

       Console::WriteLine("In try block...");

       ThrowMyException();

   }

   catch (MyException^ e)

   {

       Console::WriteLine("In catch block...");

       Console::WriteLine(e->GetType());

   }

   finally

   {

       Console::WriteLine("In finally block...");

   }

}

 

Output:

Exception handling - finally keyword usage console program output sample

 

 

 

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7

 


 

< C++ .NET Exception Handling 2 | Main | C++ .NET Exception Handling 4 >