Unit 11: Exceptions

  1. Error 12345
  2. Modern error handling
  3. Built-in exception types in C++
  4. Throw... Try... Catch...
  5. Handling the error
  6. Common error in student implementation

  7. Example code and additional resources

This week's stuff:

Error 12345

In the Olden Times it was common to give error number codes when something went wrong in a software program. A programmer would be able to take the error code and find where the error was being generated in the software - but it wasn't very helpful for the average user... or anyone without access to the code.

Modern error handling

These days it's better to use Exceptions to report errors. We can write reusable functions that throw an exception if an error occurs, and then whoever needs that function can try to call the function, and catch any exceptions that occur.

The exceptions also have a string message baked into it, so we can use this for more detailed information as to why the exception happened.

So, this is useful for several reasons:

  1. When an exception is encountered in the code, we can see what type of exception it is and use different methods to handle different types of exceptions.
  2. We can use the message feature to give a more detailed description of what went wrong so the erroneous action can be avoided in the future (from the user's perspective).

Built-in exception types in C++

Exception class docs: https://cplusplus.com/reference/exception/exception/

ExceptionDescription...ExceptionDescription
bad_alloc Exception thrown on failure allocating memory bad_cast Exception thrown on failure to dynamic cast
bad_exception Exception thrown by unexpected handler bad_function_call Exception thrown on bad call
bad_typeid Exception thrown on typeid of null pointer bad_weak_ptr Bad weak pointer
ios_base::failure Base class for stream exceptions logic_error Logic error exception
runtime_error Runtime error exception domain_error Domain error exception
future_error Future error exception invalid_argument Invalid argument exception
length_error Length error exception out_of_range Out-of-range exception
overflow_error Overflow error exception range_error Range error exception
system_error System error exception\ underflow_error Underflow error exception
bad_array_new_length Exception on bad array length

Throw... Try... Catch...

Detecting errors and THROWING exceptions

The first step of dealing with exceptions is identifying a place where an error may occur - such as a place where we might end up dividing by zero. We write an if statement to check for the error case, and then throw an exception. We choose an exception type and we can also pass an error message as a string.

int ShareCookies( int cookies, int kids )
{
  if ( kids == 0 )
  {
    throw runtime_error( "Cannot divide by zero!" );
  }

  return cookies / kids;
}

TRYING to call a function and CATCHING exceptions

Now we know that the function ShareCookies could possibly throw an exception. Any time we call that function, we need to listen for any thrown exceptions by using try.

Immediately following the try, we can write one or more catch blocks for different types of exceptions and then decide how we want to handle it.

try
{
  // Calling the function
  cookiesPerKid = ShareCookies( c, k );
}
catch( runtime_error ex )
{
  // Display the error message
  cout << "Error: " << ex.what() << endl;

  // Handling it by setting a default value
  cookiesPerKid = 0;
}

cout << "The kids get " << cookiesPerKid << " each" << endl;

Handling the error

Once you catch an error, it's up to you to decide what to do with it. For example, you could…

  • Ignore the error and just keep going
  • Write some different logic for a "plan B" backup
  • End the program because now the data is invalid

Coding with others' code: While writing software and utilizing others' code, you will want to pay attention to which functions you're calling that could throw exceptions. Often code libraries will contain documentation that specify possible exceptions thrown.

Common error in student implementation

DO THIS:

// FUNCTION THAT COULD CAUSE ERROR - Responsible for THROW
float Divide( float num, float denom )
{
  if ( denom == 0 )
  {
    throw Exception;
  }

  return num / denom;
}


int main()
{
  float n, d;
  cout << "Enter num: ";
  cin >> n;

  cout << "Enter denom: ";
  cin >> d;

  float result = 0;
  try
  {
    // CALLING "IFFY" FUNCTION - Wrap the CALL in try/catch
    result = Divide( n, d );
  }
  catch( Exception& ex )
  {
    cout << "ERROR OCCURRED!" << endl;
    return 1234;
  }

  cout << "Result: " << result << endl;

  return 0;
}

DON'T DO THIS:

float Divide( float num, float denom )
{
  try
  {
    if ( denom == 0 )
    {
      throw Exception;
    }

    return num / denom;
  }
  catch( Exception& ex )
  {
    // ...
  }
}

A common error I see students make is to put the try, catch, and throw statements all in one function. This defeats the point of even using exceptions.

Example code and additional resources

Spring 2024 lecture:

Example code: https://youtu.be/x7WYZX3cxIM


Example code: (repository)

  1. Exceptions: Divide by zero
  2. Templates and exceptions: Template log and Exceptions Game Data

Archived videos and class lectures: