C++ Exception Handling

Hello there, aspiring programmers! Today, we're going to embark on an exciting journey into the world of C++ Exception Handling. As your friendly neighborhood computer science teacher, I'm here to guide you through this important topic. So, grab your favorite beverage, get comfortable, and let's dive in!

C++ Exception Handling

What are Exceptions?

Before we start throwing and catching exceptions like seasoned jugglers, let's understand what they are. In the programming world, exceptions are unexpected events that occur during the execution of a program. They're like those surprise pop quizzes I used to give (sorry about that!) – unexpected and sometimes a bit challenging to handle.

Exceptions disrupt the normal flow of a program's instructions. They can be caused by various factors, such as:

  1. Division by zero
  2. Accessing an array out of bounds
  3. Running out of memory
  4. Trying to open a file that doesn't exist

Now, let's see how C++ allows us to manage these unexpected situations gracefully.

Throwing Exceptions

The Basics of Throwing

In C++, we use the throw keyword to raise an exception. It's like raising your hand in class when you have a question or problem. Here's a simple example:

#include <iostream>
using namespace std;

int main() {
    try {
        throw 20;
    }
    catch (int e) {
        cout << "An exception occurred. Exception Nr. " << e << endl;
    }
    return 0;
}

In this example, we're throwing an integer exception with the value 20. But don't worry, we'll catch it in just a moment!

Throwing Different Types

C++ is flexible and allows you to throw exceptions of various types. Let's look at a more practical example:

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(int a, int b) {
    if (b == 0) {
        throw runtime_error("Division by zero!");
    }
    return static_cast<double>(a) / b;
}

int main() {
    try {
        cout << divide(10, 2) << endl;  // This will work fine
        cout << divide(10, 0) << endl;  // This will throw an exception
    }
    catch (const runtime_error& e) {
        cout << "Caught exception: " << e.what() << endl;
    }
    return 0;
}

In this example, we're throwing a runtime_error exception when someone tries to divide by zero. It's like putting up a "No Division by Zero" sign in our mathematical neighborhood!

Catching Exceptions

The Basics of Catching

Now that we know how to throw exceptions, let's learn how to catch them. Catching exceptions is like being a responsible pet owner – you need to be prepared to handle whatever your code throws at you!

We use a try-catch block to catch exceptions. The try block contains the code that might throw an exception, and the catch block handles the exception if it occurs.

#include <iostream>
using namespace std;

int main() {
    try {
        int age = -5;
        if (age < 0) {
            throw "Age can't be negative!";
        }
        cout << "Age is: " << age << endl;
    }
    catch (const char* msg) {
        cerr << "Error: " << msg << endl;
    }
    return 0;
}

In this example, we're checking if the age is negative. If it is, we throw an exception with a custom error message.

Catching Multiple Exceptions

Sometimes, different types of exceptions can be thrown from the same piece of code. In such cases, we can have multiple catch blocks:

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
    try {
        int choice;
        cout << "Enter 1 for integer exception, 2 for runtime error: ";
        cin >> choice;

        if (choice == 1) {
            throw 42;
        } else if (choice == 2) {
            throw runtime_error("A wild runtime error appeared!");
        } else {
            throw "Unknown choice!";
        }
    }
    catch (int e) {
        cout << "Caught integer exception: " << e << endl;
    }
    catch (const runtime_error& e) {
        cout << "Caught runtime error: " << e.what() << endl;
    }
    catch (...) {
        cout << "Caught an unknown exception!" << endl;
    }
    return 0;
}

This example shows how we can catch different types of exceptions. The catch (...) block is a catch-all that will handle any exception not caught by the previous catch blocks. It's like having a safety net for all those unexpected surprises!

C++ Standard Exceptions

C++ comes with a set of standard exceptions that you can use in your programs. These are like the Swiss Army knives of the exception world – versatile and always ready to help!

Here's a table of some commonly used standard exceptions:

Exception Description
std::runtime_error Runtime logic errors
std::logic_error Logic errors
std::out_of_range Accessing beyond the range
std::overflow_error Arithmetic overflow
std::bad_alloc Memory allocation fails

Let's see an example using a standard exception:

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};
    try {
        cout << numbers.at(10) << endl;  // This will throw an out_of_range exception
    }
    catch (const out_of_range& e) {
        cerr << "Out of Range error: " << e.what() << endl;
    }
    return 0;
}

In this example, we're trying to access an element that's out of range in our vector. The at() function throws an out_of_range exception when this happens.

Define New Exceptions

While the standard exceptions are great, sometimes you need something more tailored to your specific needs. That's where custom exceptions come in handy!

Here's how you can define your own exception class:

#include <iostream>
#include <exception>
using namespace std;

class NegativeValueException : public exception {
public:
    const char* what() const throw() {
        return "Negative values are not allowed!";
    }
};

double squareRoot(double x) {
    if (x < 0) {
        throw NegativeValueException();
    }
    return sqrt(x);
}

int main() {
    try {
        cout << squareRoot(25) << endl;  // This will work fine
        cout << squareRoot(-5) << endl;  // This will throw our custom exception
    }
    catch (const NegativeValueException& e) {
        cerr << "Error: " << e.what() << endl;
    }
    return 0;
}

In this example, we've created a custom NegativeValueException class. We use it in our squareRoot function to throw an exception when someone tries to calculate the square root of a negative number.

And there you have it, folks! We've covered the basics of C++ Exception Handling. Remember, exceptions are your friends. They help you write more robust and error-resistant code. Keep practicing, and soon you'll be handling exceptions like a pro!

Happy coding, and may your exceptions always be caught!

Credits: Image by storyset