C++ Exception Throwing Mechanism

The C++ exception handling mechanism allows a program to transfer control from the point of error to specialized error handling code when a runtime error occurs.

The core mechanism is based on three keywords: throw, try, and catch.

Key components:

1. throw

When an error is detected, an exception object is thrown using throw. Any type can be thrown (basic types, class objects, pointers, etc.), but it is recommended to use standard exception classes.

2. try

Wraps the code block that may throw an exception. Once an exception is thrown, the program immediately exits the try block.

3. catch

Catches and handles specific types of exceptions. catch blocks are matched in order, supporting multiple type catches and ellipsis () to catch all exceptions.

Execution flow:

1. An exception is thrown in the try block

2. Exit the current scope (stack unwinding, calling destructors of local objects)

3. Match catch blocks in order

4. If a match is found, execute the handling code; otherwise, continue stack unwinding

5. If no matching handler is found, call std::terminate() to terminate the program

Code Example

Example 1: Basic Exception Handling

#include <iostream>#include <stdexcept> // Standard exception definitions
double divide(double a, double b) {    if (b == 0) {        // Throw standard exception class object        throw std::runtime_error("Division by zero!");    }    return a / b;}
int main() {    try {        double result = divide(10, 0);        std::cout << "Result: " << result << "\n";    }    catch (const std::runtime_error& e) { // Catch specific exception        std::cerr << "Error: " << e.what() << "\n";    }    catch (...) { // Catch all other exceptions        std::cerr << "Unknown error occurred!\n";    }    return 0;}

Output: Error: Division by zero!

Example 2: Custom Exception Class

#include <iostream>#include <string> // Custom exception class (inherits std::exception)
class InvalidInputException : public std::exception {    std::string msg;public:    InvalidInputException(const std::string& s) : msg(s) {}    const char* what() const noexcept override {        return msg.c_str();    }};
void processAge(int age) {    if (age < 0 || age > 120)     {        throw InvalidInputException("Invalid age: " + std::to_string(age));    }    std::cout << "Age processed: " << age << "\n";}
int main() {    try     {        processAge(-5); // Trigger exception    }    catch (const InvalidInputException& e) {        std::cerr << "Custom Error: " << e.what() << "\n";    }    catch (const std::exception& e) { // Catch base class exception        std::cerr << "Standard Error: " << e.what() << "\n";    }    return 0;}

Output: Custom Error: Invalid age: -5

Example 3: Stack Unwinding and Resource Management (RAII)

#include <iostream>#include <memory>
class Resource {public:    Resource() { std::cout << "Resource acquired\n"; }    ~Resource() { std::cout << "Resource released\n"; }};
void riskyOperation() {    Resource res; // RAII object    throw std::logic_error("Operation failed!");    // When the exception is thrown, res will be automatically destructed}
int main() {    try {        riskyOperation();    }    catch (const std::exception& e) {        std::cerr << "Caught: " << e.what() << "\n";    }    return 0;}

Output:

Resource acquired

Resource released

Caught: Operation failed!

Key Considerations:

1. Exception Safety

Use RAII (Resource Acquisition Is Initialization) to manage resources (such as smart pointers, file handles, etc.) to ensure that resources are not leaked when exceptions occur.

2. Exception Types

Prefer using standard exceptions (defined in stdexcept):

std::runtime_error: Runtime error

std::logic_error: Program logic error

std::out_of_range: Out of bounds access

Custom exceptions should inherit from std::exception.

3. Performance Considerations

The exception handling mechanism has no overhead on the normal path. The overhead is significant when throwing exceptions (stack unwinding, type matching, etc.), so it should not be used for regular control flow.

4. Best Practices

Arrange catch blocks from specific to abstract by exception type. Use const& to catch exceptions to avoid copying, and resource cleanup should be done through destructors rather than catch blocks.

// Correct: Sort by derived class to base class
try { /* ... */ }catch (const MyException& e) { ... }  // Specific exception
catch (const std::exception& e) { ... } // General exception
catch (...) { ... }                   // Fallback handling

By using the exception mechanism appropriately, the robustness and maintainability of the code can be significantly improved, but care should be taken to avoid overuse, which can lead to performance degradation or code structure complexity.

Leave a Comment