C++: Try and Catch

Looking for in-depth C tutorials? Try SourceCrunch.

When programs become more complex, it is often the case that different errors and things that would create problems should be handled (for example runtime errors). The subject of error handling, or exception handling, is very large and complicated, and often solutions can be accomplished in a number of ways. Some people swear by simply having if-statements that stop execution in cases that could be problematic, and some people like having a custom function that can deal with errors by being passed some kind of number or code. C++ provides a pretty neat way to handle exceptional circumstances natively though, and this functionality is provided through three main keywords: try, throw, and catch.

The general idea is that code which has the potential to go wrong in some way is wrapped in a try block, using the throw keyword when it encounters an exception, and then one of the catch blocks which follows the try block can handle the exception in some way. A nice example to demonstrate the use of this might be a simple application which divides two numbers, like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

int divide_numbers(int a, int b)
{
    return a/b;
}

int main()
{
    int a, b;
    cout << "Number one: ";
    cin >> a;
    cout << "Number two: ";
    cin >> b;

    cout << divide_numbers(a, b);

    return 0;
}

The above would work perfectly fine (not to say that it couldn't be improved by passing-by-reference and various other things), however what would it do if the user entered 'b' as '0', since dividing by 0 is "impossible"? The answer is that it would probably give some kind of runtime error or exception. In a "real world" application, the user probably wouldn't know what this meant and it'd be awfully confusing for everybody involved in the process of trying to help fix the problem. A nice way to deal with this might be to use try, throw, and catch! We could surround the 'return' code in the function in a try block, use an if-statement to throw an exception if 'b' is 0, and then catch the exception and cout some kind of error. So conceptually, something like the following:

1
2
3
4
5
6
7
8
try
{
    if(b==0)
        //Throw an exception

    return a/b;
}
//Catch the exception

The throw statement behaves much like return. You can (and usually do) pass it a value, and then this value is passed as a parameter into the catch block. Often either int or string-esque data-types are thrown as they can either represent an error code or text explaining the error, however it isn't uncommon for some custom class to be thrown either. In this example, we're just going to throw an integer error code and then output the error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int divide_numbers(int a, int b)
{
    try
    {
        if(b==0)
            throw 1;

        return a/b;
    }
    catch(int err_code)
    {
        cout << "ERROR CODE " << err_code;
    }
}

We also ought to return something, as currently nothing is returned if division by 0 is attempted. Since execution usually continues like nothing happens after "try-catch" sections, we can simply place some kind of error return after all of this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int divide_numbers(int a, int b)
{
    try
    {
        if(b==0)
            throw 1;
        return a/b;
    }
    catch(int err_code)
    {
        cout << "ERROR CODE " << err_code;
    }
    //Continue doing whatever afterwards like normal

    return -1; //Uh oh!
}

This is not, however, an ideal solution. Firstly, we're having to take up memory copying the error code as a parameter into the catch block - we could solve this by making the block take a reference: catch(int& err_code), and secondly we're still outputting '-1' to the user, even if we know these results are not accurate.

To resolve the second of these issues, we can make use of the fact that throw works its way back to find the "closest" try-catch block that it can. In this case, it would be perfectly fine (and in many ways better) for our function to do the throwing , however to avoid putting the try-catch block in there. If we put the function call inside a try-catch block, the throw would still trigger any matching catch blocks! Take, for example, the following, which prevents output if an integer exception is thrown:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int divide_numbers(int a, int b)
{
    if(b==0)
        throw 1;
    return a/b;
}

int main()
{
    int a, b;

    cout << "One: ";
    cin >> a;
    cout << "Two: ";
    cin >> b;

    try
    {
        cout << divide_numbers(a, b);
    }
    catch(int& code)
    {
        cout << "ERROR CODE: " << code;
    }
    //Continue doing whatever afterwards like normal

    return 0;
}

In a case like the above, we may not, however, know that an exception with an integer will be thrown. If an exception with an integer is thrown, then brilliant, output that integer as an error code, but if any other kind of exception is thrown, we currently aren't dealing with it. A default catch can be made by giving an ellipsis as a parameter - this should be at the bottom of the chain if there are multiple catchs as it will handle any and all exceptions. An example implementation is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int divide_numbers(int a, int b)
{
    if(b==0)
        throw 1;
    return a/b;
}

int main()
{
    int a, b;

    cout << "One: ";
    cin >> a;
    cout << "Two: ";
    cin >> b;

    try
    {
        cout << divide_numbers(a, b);
    }
    catch(int& code)
    {
        cout << "ERROR CODE: " << code;
    }
    catch(...)
    {
        cout << "An unknown error has occurred.";
    }
    //Continue doing whatever afterwards like normal

    return 0;
}

There are some cases in which it's especially good practice to use a try-catch block. Dynamically allocating memory is a prime example of this -- if we allocate memory using the new keyword and not enough memory is available, an exception of type "bad_alloc" will be thrown. This inherits from the base "exception" class which can be found in <exception>, which is a standard class for exceptions. Provoking the what member function on an "exception" object should tell us what its problem was, and this can be seen by trying to allocate a large amount of data - 100000000000000 was enough for my machine:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
    try
    {
        int* myarray = new int[100000000000000];
        delete [] myarray;
    }
    catch(exception& e) //Takes a reference to an 'exception' object
    {
        cout << "Error allocating memory: " << e.what() << endl;
    }
}

If you're interested in using standard "exception" objects for your own exception handling using try-catch (which isn't a bad idea), the class is part of the 'std' namespace, and much more information about it can be found from various online resources.