Computer scienceProgramming languagesC++Errors, debugging, and exceptions

Exception Handling

8 minutes read

Exception handling is a crucial aspect of modern programming languages, including C++. It allows developers to handle unexpected or erroneous situations gracefully and to prevent program crashes. In this topic, you'll explore the concept of exception handling in C++, including its syntax, best practices, and practical examples.

What are exceptions

In C++, exceptions represent abnormal events during program execution, such as errors or unexpected conditions. This disrupts the normal flow of the program and, if not handled, can lead to crashes or unexpected behavior. Exceptions are created when different types of errors are encountered, disrupting the program's normal flow.

C++ provides a powerful mechanism for dealing with exceptions, allowing you to detect and gracefully recover from these exceptional conditions. This means it allows you to respond to exceptional conditions in a controlled and structured manner, as opposed to simply crashing the program. Exception handling involves two main components: throwing exceptions and catching exceptions, which we'll discuss later.

Syntax of Exception Handling

In C++, exception handling is done using three keywords: try, catch, and throw.

  • try: The try block contains the code that may throw an exception. One or morecatch blocks follow it.

  • catch: Each catch block is associated with a specific type of exception. When an exception of that type is thrown within the try block, the corresponding catch block is executed.

  • throw: The throw keyword is used to throw an exception explicitly. An exception object or a built-in type can follow it.

Let's simplify these concepts with a syntax and a basic code example:

try {
    // Code that may throw an exception
} catch (ExceptionType e) {
    // Code to handle the exception
}

The code enclosed within the try block is where you place the code that may potentially throw an exception. If an exception of the specified ExceptionType (a user-defined or standard exception type) is thrown within the try block, the control will transfer to the corresponding catch block. Inside the catch block, you can handle the exception. This is where you can write code to deal with the exceptional situation. You can access information about the exception through the ExceptionType object (e in this case).

Why exception handling

Exception handling in C++ is a way to deal with errors in a program without making it crash. Instead of using a bunch of if-else statements to check for errors, we use exception handling, which makes our code easier to read and maintain.

Think of it like this: Imagine you're baking a cake and have a recipe with instructions. If you follow the instructions carefully, everything goes smoothly. But what if you accidentally drop an egg on the floor? You wouldn't want your entire cake-making process to stop, right? You'd want to handle the egg-drop situation and continue making the cake.

In C++, exception handling lets you do something similar. You can continue with your program even if something unexpected, like an error, happens. It's like putting that broken egg aside and still finishing the cake.

Here are some key points about exception handling in C++:

  1. Hierarchy of Exceptions: You can group different types of exceptions based on their characteristics. It's like putting all the cake-related errors in one box and all the frosting-related errors in another. This makes it easier to handle them separately.

  2. Throwing Exceptions: When something goes wrong, you can "throw" an exception. It's like saying, "Hey, we have a problem here!" You can throw built-in problems (like a "cake batter too thick" problem) or make up our custom problems (like a "ran out of chocolate chips" problem).

  3. Compile Time vs. Run Time Errors: In C++, some errors are like typos in the recipe; you catch them while reading the recipe (compile time errors). Others are like accidentally using salt instead of yeast; you only discover them while baking (run time errors). These run time errors are what we call exceptions because we didn't see them coming during the recipe reading (compile time).

So, to sum it up, exception handling in C++ helps us keep our program running smoothly even when unexpected problems occur. You can define different types of problems, throw them when needed, and continue baking your program's cake.

Throwing and catching exceptions

Let's say you're writing a simple program to divide two numbers, and you want to handle the case where the divisor is zero, which would result in a division by zero error. Here's how you can do it:

#include <iostream>
using namespace std;

int main()
{
    int numerator = 42;
    int denominator = 0;
    int result = 0;

    /*
    Using a try-catch block because in division, the denominator can be zero.
    So, we must handle the zero division inside the try block.
    */
    try
    {
        if (denominator == 0){

            // If the denominator is zero, then we must throw an exception.
            throw "Division by zero is not allowed!";
        }

        result = int(numerator / denominator);

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

    // Printing the thrown exception message.
    catch (const char* errorMessage)
    {
        cout << "Error: " << errorMessage << endl;
    }

    return 0;
}

The above example calculates the result of dividing two numbers, numerator and denominator, while protecting against division by zero. Inside a try block, it checks if the denominator is zero, and if so, it throws an exception with the message "Division by zero is not allowed!" Otherwise, it performs the division and displays the result. If an exception is thrown, the catch block catches it and prints the corresponding error message. This approach ensures that the program gracefully handles errors, preventing crashes and allowing for error reporting.

When an exception is thrown in C++, code execution within the try block is abruptly stopped, and any code following the throw statement is bypassed. The program then searches for a corresponding catch block to manage the exception, ensuring the control flow is redirected for proper error handling without program termination.

Examples

Let's take examples to understand how you can implement exception handling in C++:

Example:

#include <iostream>
using namespace std;

int main()
{
   int value = 99;
   cout << "Before entering the try block." << endl;

   try
   {
      cout << "Inside the try block." << endl;
      // Throwing an exception if the
      // 'value' is less than 100.

      if (value < 100){

         // Throwing the 'value' as an exception because it's less than 100.
         throw value;
         cout << "After the exception is thrown." << endl;
          
      }
   }

   // Catching the 'value' thrown by the 'throw' statement from the try block.
   catch (int errorValue)
   {
      cout << "Exception caught in the catch block. The error value is: " << errorValue << endl;
   }
   return 0;
}

In this example, we initialize an integer variable 'value' to 99 and then use exception handling. Inside a try block, we check if value is less than 100. If it is, we throw an exception using 'throw,' which directs the program to the catch block. The catch block catches the exception (an integer value) and displays an error message along with the thrown value. Notably, code after the throw statement in the try block is skipped when an exception occurs. This example illustrates exception handling and how to manage situations when a specific condition is unsatisfied.

Example:

#include <iostream>
using namespace std;

int main() {
    int age;
    cout << "Enter your age: ";
    cin >> age;

    try 
    {
        if (age < 18) {
            throw "Invalid age. Age must be greater than 18.";
        }

        cout << "Age is valid: " << age << " years" << endl;
    } 
    
    catch (const char* errorMessage) 
    {
        cout << "Error: " << errorMessage << endl;
    }

    return 0;
}

The above code prompts the user to input their age and then uses exception handling to check if the age is valid. If the entered age is under 18, it throws an exception with the message "Invalid age. Age must be greater than 18." Otherwise, if the age is valid, it displays a message indicating its validity. The catch block catches exceptions of type const char* and prints an error message if an exception is thrown due to an invalid age.

There is another way to implement exception handling using the C++ Standard library. For now, we've used custom exceptions and custom messages. You'll learn the advanced concepts in other topics.

Conclusion

Exceptions are abnormal events that can disrupt a program's normal flow and are managed using the try, catch, and throw keywords. Exception handling allows for error reporting, ensuring that programs don't crash when encountering exceptional conditions. By throwing and catching exceptions, developers can handle errors and continue program execution smoothly.

3 learners liked this piece of theory. 1 didn't like it. What about you?
Report a typo