Skip to main content

Mastering C++ Exception Handling for Robust Code

·718 words·4 mins
C++ Exception Handling RAII
Table of Contents

C++ provides a rich set of features for building powerful, reliable applications, and one of its most important mechanisms is exception handling. With try, catch, and throw, C++ enables structured error handling that improves correctness, safety, and maintainability — especially when combined with RAII.

This guide takes a deep, practical look at exceptions, including how they work, when to use them, standard exception types, and best practices for writing exception-safe code.

1. Introduction to Exception Handling
#

An exception represents an unexpected situation during runtime — such as divide-by-zero, invalid arguments, failed operations, or resource errors.

C++ handles these conditions using:

  • try: wrap code that may fail
  • throw: signal failure
  • catch: handle the failure

Basic Example
#

#include <iostream>
#include <stdexcept>

int main() {
    try {
        int a = 10, b = 0;
        if (b == 0) {
            throw std::runtime_error("Division by zero error");
        }
        int result = a / b;
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
}

The exception transfers control from the throw point to the nearest compatible catch block.

2. Exception Class Hierarchy
#

C++ exceptions are objects — typically derived from std::exception.

Custom Exception Example
#

#include <iostream>
#include <stdexcept>

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "MyException occurred";
    }
};

int main() {
    try {
        throw MyException();
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
}

Deriving from std::exception:

  • Integrates with standard handlers
  • Provides a uniform interface via .what()
  • Supports polymorphic catching

3. Throwing Exceptions
#

You can throw exceptions from anywhere in your code.

Example
#

#include <iostream>
#include <stdexcept>

void someFunction(bool errorCondition) {
    if (errorCondition) {
        throw std::runtime_error("Something went wrong in someFunction");
    }
}

int main() {
    try {
        someFunction(true);
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
}

Thrown exceptions propagate upward until a matching catch is found.

4. RAII: Resource Acquisition Is Initialization
#

RAII ensures resource safety — one of the strongest guarantees of C++ — especially during exceptions. When an exception is thrown, destructors for stack objects still run, releasing resources reliably.

RAII Example
#

#include <iostream>
#include <stdexcept>
#include <cstdio>

class FileHandler {
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
        if (!file) {
            throw std::runtime_error("Failed to open file");
        }
        std::cout << "File opened successfully." << std::endl;
    }

    ~FileHandler() {
        if (file) {
            fclose(file);
            std::cout << "File closed successfully." << std::endl;
        }
    }

private:
    FILE* file;
};

int main() {
    try {
        FileHandler file("example.txt");
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
}

RAII ensures:

  • Resources are released safely.
  • No leaks occur, even if exceptions are thrown.
  • Error paths and success paths behave consistently.

5. Standard Exception Types
#

C++ provides many built-in exceptions, including:

  • std::runtime_error
  • std::logic_error
  • std::out_of_range
  • std::invalid_argument
  • std::bad_alloc

All derive from std::exception.

Example
#

#include <iostream>
#include <stdexcept>

int main() {
    try {
        throw std::runtime_error("A critical runtime issue occurred");
    } catch (const std::exception& e) {
        std::cerr << "Caught standard exception: " << e.what() << std::endl;
    }
}

Catching by reference to std::exception handles all derived standard exceptions.

6. Exceptions and Performance
#

While exceptions provide safety and clarity, they are not free:

  • Throwing is expensive (stack unwinding, object allocation, RTTI lookup).
  • Use exceptions for errors, not normal control flow.
  • For ultra-hot code paths, error codes may be preferred.

Exception presence has little cost — but actually throwing does.

7. Best Practices
#

✔ Catch by reference
#

Use catch (const std::exception& e).

✔ Avoid catch (...) unless necessary
#

It hides too much and makes debugging harder.

✔ Use exceptions only for exceptional cases
#

Do not use exceptions for branching logic.

✔ Ensure exception-safe code
#

Provide strong or basic exception guarantees:

  • No leaks
  • No partial updates without rollback
  • RAII everywhere

✔ Never throw in a destructor (unless you really know why)
#

8. Conclusion
#

Exception handling is an essential part of modern C++ development. By understanding try/catch, designing custom exception types, combining exceptions with RAII, and following best practices, you can build programs that are both robust and maintainable.

C++ gives you the tools — and with proper discipline, exception handling becomes a powerful ally in writing high-quality software.

Related

How to Write a Qualified C++ Class
·613 words·3 mins
C++
11 Digital Filtering Algorithms in C++
·815 words·4 mins
C++ Digital Filtering Algorithms
非常强大的高性能 C++ 服务器引擎
·72 words·1 min
程序 C++