throw, try and catch in C++

Learn how we can use the throw, try and catch statements to let our program manage unexpected situations
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

52.jpg
Ryan McCombe
Ryan McCombe
Posted

Assertions give us a way to check for errors, but they have two limitations:

  • They do not give us a way to recover from errors
  • Because of that, they’re designed to be removed from the code in the builds we release to users, so they have no effect at all in production builds

To address these two limitations, we have dedicated key words within C++, and many similar languages: throw, try and catch.

Throwing errors with throw

Previously, we wrote this function using assert:

#include <iostream>
#include <cassert>

int Divide(int x, int y) {
  assert(y != 0 && "Cannot divide by zero");
  return x/y;
}

int main() {
  Divide(5, 0);
}

Running this program, our crash dump would have looked something like this:

main.cpp:5: int Divide(int, int):
Assertion `y != 0 && "Cannot divide by zero"' failed.

Let's rewrite this example using throw instead:

#include <iostream>

int Divide(int x, int y) {
  if (y == 0) throw "Cannot divide by zero";
  return x/y;
}

int main() {
  Divide(5, 0);
}

Our crash dump now looks like this:

terminate called after throwing an instance of 'char const*'

Our program terminates at the same point, although the error message is now less helpful. However, the benefit of using throw is that we can catch those errors, and recover from them.

Exceptions

An error that occurs in software is frequently called an exception. Using the throw keyword in this way is often referred to as “throwing an exception”

Catching Exceptions with try and catch

In any function of our program, we can introduce an error boundary. Error boundaries catch any errors that were thrown as the result of executing a block of code.

They are comprised of two components - a try block and a catch block.

Within the try block, we place the code that might throw an exception.

Within the catch block, we place the code that will be called if an exception was indeed thrown.

int main() {
  try {
    Divide(5, 0);
  } catch (...) {
    std::cout << "An error was caught";
  }
  std::cout << " but the program recovered";
}
An error was caught but the program recovered

As with most block statements within functions, we can elect to return early from a catch statement:

int main() {
  try {
    Divide(5, 0);
  } catch (...) {
    std::cout << "An error was caught";
    return -1;
  }
  std::cout << " but the program recovered";
}
An error was caught

Catching Specific Exceptions

Within the above example, the ... syntax within catch (...) indicates we want to catch all errors. This is generally not what we want - typically, we will want to catch errors of specific types. This gives us the added benefit of being able to access the actual exception that was thrown and inspect it.

We can throw any type of object as an exception.

In this case, we threw a simple primitive - the literal expression: "Cannot divide by zero". As indicated in the crash dump, this is an instance of const char*, ie, a C-style array of characters.

To access this object, we update our catch code to indicate it is only interested exceptions that have a type of const char*.

We also give that object a name so we can access it within the block of our catch statement. In this example, we called it simply Error:

int main() {
  try {
    Divide(5, 0);
  } catch (const char* Error) {
    std::cout << "An error was caught: "
              << Error << std::endl;
  }
  std::cout << "But the program recovered";
}
An error was caught: Cannot divide by zero
But the program recovered

Catching Multiple Exception Types

We can catch multiple distinct exception types by using multiple catch statements:

int Divide(int x, int y) {
  if (y == 0) throw "Cannot divide by zero";
  if (y == 1) throw -1;
  if (y == 2) throw true;
  return x/y;
}

int main() {
  try {
    Divide(5/0);
    Divide(5/1);
    Divide(5/2);
  } catch (const char* Error) {
    // A const char* was thrown
  } catch (int Error) {
    // An int was thrown
  }
}

In this example, the const char* error thrown by the first call to Divide will be caught by the first catch statement.

The int error thrown by the second call to Divide will be caught by the second catch statement.

The bool error thrown by the third call to Divide will not be caught. This is frequently referred to as an “uncaught exception”. If our program throws an exception that is never caught, it will terminate.

In this case, the crash dump might report something like:

terminate called after throwing an instance of 'bool'

Custom Exception Types

Generally, we don’t want to be throwing arbitrary objects like strings and integers. Instead, we should use a type of object dedicated to this purpose.

The standard library comes with a range of such types, that all inherit from a base std::exception class. We can also create our own exception types, specific to our use case, by creating our own classes that inherit from std::exception

This would give us a standardized, structured way of creating exceptions. We will cover this in the next lesson

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Exceptions and Error Handling
7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ Exception Types

In this lesson, we will learn about the std::exception types that come with the C++ standard library. We will also learn how to create and use our own custom exception types.
vb5.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved