Skip to main content

C++ Exception Handling: Modern try-catch-throw in Practice

·489 words·3 mins
C++ Catch Throw Try RAII
Table of Contents

Background
#

In C++ programs, errors such as invalid input, missing files, or failed operations can occur unexpectedly. Proper handling of these errors is critical to avoid crashes and maintain program stability. C++ exception handling provides a structured mechanism to detect and respond to runtime errors.


Modern Exception Handling Concepts
#

1. throw: Raising Exceptions
#

Use throw to signal an error condition. Always throw exceptions derived from std::exception for compatibility and clarity.

#include <stdexcept>
#include <string>

void processInput(int value) {
    if (value < 0) {
        throw std::invalid_argument("Value must be non-negative");
    }
    // Continue processing...
}

2. try: Protected Code Block
#

Wrap potentially risky code inside a try block. Only code that may fail should go inside.

try {
    processInput(-5);
} catch (const std::invalid_argument& e) {
    std::cerr << "Input error: " << e.what() << "\n";
}

3. catch: Handling Exceptions
#

Catch specific exception types first, followed by a generic handler if needed. Prefer const references to avoid unnecessary copies.

try {
    // Code that may throw exceptions
    processInput(-10);
} catch (const std::invalid_argument& e) {
    std::cerr << "Invalid argument: " << e.what() << "\n";
} catch (const std::runtime_error& e) {
    std::cerr << "Runtime error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cerr << "Other exception: " << e.what() << "\n";
} catch (...) {
    std::cerr << "Unknown exception occurred\n";
}

4. RAII: Prevent Resource Leaks
#

Use RAII (Resource Acquisition Is Initialization) to manage resources automatically. This ensures resources are released even if an exception occurs.

#include <fstream>
#include <iostream>
#include <string>

void readFile(const std::string& filename) {
    std::ifstream file(filename); // RAII automatically closes the file
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file: " + filename);
    }

    std::string line;
    while (std::getline(file, line)) {
        std::cout << line << "\n";
    }
}

int main() {
    try {
        readFile("data.txt");
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << "\n";
    }
}

5. Custom Exception Classes
#

For more complex applications, define custom exception classes derived from std::exception:

#include <exception>
#include <string>

class MyException : public std::exception {
public:
    explicit MyException(const std::string& message) : msg_(message) {}
    const char* what() const noexcept override {
        return msg_.c_str();
    }
private:
    std::string msg_;
};

void riskyOperation() {
    throw MyException("Something went wrong in riskyOperation!");
}

int main() {
    try {
        riskyOperation();
    } catch (const MyException& e) {
        std::cerr << "Custom exception caught: " << e.what() << "\n";
    }
}

Best Practices in Modern C++
#

  1. Always derive from std::exception for consistency.
  2. Throw by value, catch by const reference.
  3. Do not use exceptions for control flow.
  4. Use RAII and smart pointers to manage dynamic resources safely.
  5. Catch specific exceptions first and provide meaningful error messages.

Conclusion
#

By combining try, catch, throw, and RAII, modern C++ provides a robust framework for handling runtime errors. This leads to safer, more maintainable, and readable code. Use exceptions judiciously, design specific exception types, and leverage RAII to prevent resource leaks and unexpected crashes.

Related

预定义宏的神秘面纱
·172 words·1 min
程序 C++ Predefined Macros
Deep Copy vs Shallow Copy in C++
·543 words·3 mins
C++ Deep Copy Shallow Copy
11 Digital Filtering Algorithms in C++
·815 words·4 mins
C++ Digital Filtering Algorithms