C++ Exception Handling

Managing errors and unexpected situations

⚠️ What is Exception Handling?

Exception handling in C++ manages runtime errors gracefully. It uses try-catch blocks to detect and handle errors without crashing your program completely.


try {
    int result = 10 / 0;  // This will cause an error
} catch (exception& e) {
    cout << "Error caught: " << e.what();
}
                                    

Exception Concepts

🎯

Try Block

Code that might cause errors

try {
    // Risky code here
    int x = riskyFunction();
}
🛡️

Catch Block

Handle specific exceptions

catch (int e) {
    cout << "Integer error: " << e;
}
🚀

Throw Statement

Trigger custom exceptions

if (error_condition) {
    throw "Something went wrong!";
}
🔄

Multiple Catches

Handle different error types

catch (int e) { }
catch (string e) { }
catch (...) { }

🔹 Basic Try-Catch

Try‑catch blocks handle exceptional conditions, preventing crashes and enabling graceful recovery. Code in the try block is monitored; if an exception is thrown, matching catch blocks execute. This separates error handling from normal logic, improving code organization and robustness. The age‑validation example catches a custom exception for negative input. By reporting the error and continuing, the program maintains stability. Try‑catch allows managing expected exceptional cases without terminating, enhancing user experience and system resilience.

#include <iostream>
using namespace std;

int main() {
    try {
        int age;
        cout << "Enter your age: ";
        cin >> age;
        
        if (age < 0) {
            throw "Age cannot be negative!";
        }
        if (age > 150) {
            throw 999;  // Throw an integer
        }
        
        cout << "Your age is: " << age << endl;
    }
    catch (const char* msg) {
        cout << "String Error: " << msg << endl;
    }
    catch (int errorCode) {
        cout << "Integer Error Code: " << errorCode << endl;
    }
    
    cout << "Program continues..." << endl;
    return 0;
}

Output (if age = -5):

Enter your age: -5
String Error: Age cannot be negative!
Program continues...

🔹 Standard Exceptions

C++ provides standard exception classes like std::out_of_range, std::invalid_argument, and std::runtime_error to handle specific error conditions. These built-in exception types convey distinct error categories, enabling catch blocks to be more precise and targeted. Using standard exceptions promotes consistency and interoperability across libraries and application code, allowing different components to share common error-handling mechanisms. This standardization makes debugging easier, reduces code duplication, and ensures your program handles errors predictably and reliably throughout its execution.

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

int main() {
    try {
        vector<int> numbers = {1, 2, 3};
        
        // This will throw out_of_range exception
        cout << numbers.at(10) << endl;
    }
    catch (out_of_range& e) {
        cout << "Out of range error: " << e.what() << endl;
    }
    catch (exception& e) {
        cout << "General error: " << e.what() << endl;
    }
    
    try {
        string text = "Hello";
        int number = stoi("abc");  // Invalid conversion
    }
    catch (invalid_argument& e) {
        cout << "Invalid argument: " << e.what() << endl;
    }
    
    return 0;
}

Output:

Out of range error: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)
Invalid argument: stoi

🔹 Custom Exception Classes

User-defined exceptions extend the standard exception hierarchy for application-specific errors and edge cases. By inheriting from std::exception, custom exception classes integrate seamlessly with existing catch blocks and error-handling mechanisms. This approach allows you to create richer error information, provide meaningful error messages, and implement tailored handling for domain-specific failure modes. Custom exceptions make your code more maintainable, expressive, and easier to debug by clearly distinguishing between different types of errors your application might encounter.

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

// Custom exception class
class DivisionByZeroException : public exception {
public:
    const char* what() const throw() {
        return "Division by zero is not allowed!";
    }
};

class NegativeNumberException : public exception {
private:
    string message;
public:
    NegativeNumberException(const string& msg) : message(msg) {}
    const char* what() const throw() {
        return message.c_str();
    }
};

double divide(double a, double b) {
    if (b == 0) {
        throw DivisionByZeroException();
    }
    if (a < 0 || b < 0) {
        throw NegativeNumberException("Negative numbers not allowed in this operation");
    }
    return a / b;
}

int main() {
    try {
        cout << "10 / 2 = " << divide(10, 2) << endl;
        cout << "10 / 0 = " << divide(10, 0) << endl;
    }
    catch (DivisionByZeroException& e) {
        cout << "Division Error: " << e.what() << endl;
    }
    catch (NegativeNumberException& e) {
        cout << "Negative Number Error: " << e.what() << endl;
    }
    
    return 0;
}

Output:

10 / 2 = 5
Division Error: Division by zero is not allowed!

🔹 Exception Handling Best Practices

Guidelines include throwing by value, catching by reference, and avoiding exceptions in destructors. Exceptions should represent exceptional conditions, not control flow. Resource management via RAII ensures cleanup even when exceptions occur. Proper exception design yields maintainable, error‑resistant code.

Best Practices:

  • Catch specific exceptions first - More specific catches before general ones
  • Use standard exceptions - When possible, use built-in exception classes
  • Don't catch and ignore - Always handle or log exceptions
  • Clean up resources - Use RAII or finally-like patterns
  • Throw by value, catch by reference - Avoid slicing and copying

Common Exception Types:

  • std::runtime_error - Runtime problems
  • std::logic_error - Logic problems in code
  • std::out_of_range - Array/vector bounds errors
  • std::invalid_argument - Invalid function arguments
  • std::bad_alloc - Memory allocation failures

🧠 Test Your Knowledge

Which keyword is used to manually trigger an exception?