Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

An exception is an error condition — often outside the program’s direct control — that prevents the program from continuing along its normal execution path. Object creation, file I/O, network operations, and calls into external libraries are all potential sources of exceptions. Robust C++ programs anticipate these failures and handle them gracefully. The Microsoft C++ compiler (MSVC) supports three exception mechanisms: standard C++ exceptions (try/catch/throw), Windows Structured Exception Handling (SEH), and MFC exceptions. For new C++ code, always prefer standard C++ exception handling — it is type-safe, portable, and guarantees that destructors run during stack unwinding.

Why Exceptions over Error Codes

In C-style programming, errors are typically communicated through return codes or global variables like errno. This approach has significant drawbacks: callers can silently ignore error codes, error handling is tangled with normal logic, and intermediate functions must pass codes up the stack even if they have nothing useful to do with them. C++ exceptions address all of these problems:
  • An unhandled exception terminates the program — you cannot silently ignore it
  • Exceptions jump directly to the first matching catch block, skipping intervening functions
  • Stack unwinding automatically destroys all local objects (invoking their destructors)
  • The code that detects the error is cleanly separated from the code that handles it

Basic try / catch / throw

#include <stdexcept>
#include <limits>
#include <iostream>

void MyFunc(int c)
{
    if (c > std::numeric_limits<char>::max())
    {
        throw std::invalid_argument("MyFunc argument too large.");
    }
    // normal processing ...
}

int main()
{
    try
    {
        MyFunc(256);   // throws std::invalid_argument
    }
    catch (const std::invalid_argument& e)
    {
        std::cerr << "Caught: " << e.what() << "\n";
        return -1;
    }
    return 0;
}
Execution of throw immediately leaves the current scope. The runtime unwinds the stack, calling destructors for all local objects, until it finds a catch block whose parameter type matches the thrown type.
Throw exceptions by value and catch them by const reference. Catching by value causes unnecessary copying (and can slice polymorphic objects). Catching by pointer requires the caller to manage memory.

Catching Multiple Exception Types

#include <stdexcept>
#include <iostream>

void riskyOperation(int x)
{
    if (x < 0)
        throw std::underflow_error("Value is negative");
    if (x > 1000)
        throw std::overflow_error("Value too large");
    if (x == 42)
        throw std::runtime_error("Magic number rejected");
}

int main()
{
    for (int val : {-1, 2000, 42, 7})
    {
        try {
            riskyOperation(val);
            std::cout << "OK: " << val << "\n";
        }
        catch (const std::underflow_error& e) {
            std::cerr << "Underflow: " << e.what() << "\n";
        }
        catch (const std::overflow_error& e) {
            std::cerr << "Overflow:  " << e.what() << "\n";
        }
        catch (const std::runtime_error& e) {
            std::cerr << "Runtime:   " << e.what() << "\n";
        }
        catch (...) {
            std::cerr << "Unknown exception\n";
        }
    }
}
catch (...) is a catch-all handler. Place it last — catch blocks are evaluated in order, and the first matching type wins.

Standard Exception Hierarchy

All standard library exceptions derive from std::exception, which is defined in <exception>:
  • what() — returns a C-string description of the error
  • All standard exceptions derive from this class
catch (const std::exception& e) {
    std::cerr << e.what() << "\n";  // works for any standard exception
}
Subclasses:
  • std::invalid_argument — invalid argument passed to a function
  • std::domain_error — mathematical domain error
  • std::length_error — exceeded maximum allowed size
  • std::out_of_range — index or value out of valid range
std::vector<int> v{1, 2, 3};
v.at(10);   // throws std::out_of_range
Subclasses:
  • std::overflow_error — arithmetic overflow
  • std::underflow_error — arithmetic underflow
  • std::range_error — result outside representable range
  • std::system_error (C++11) — OS-level errors with an error code
#include <system_error>
throw std::system_error(
    std::make_error_code(std::errc::no_such_file_or_directory));
Thrown by new when memory cannot be allocated.
try {
    auto p = new int[std::numeric_limits<size_t>::max()];
}
catch (const std::bad_alloc& e) {
    std::cerr << "Out of memory: " << e.what() << "\n";
}

Custom Exception Classes

Derive your own exception classes from std::exception or one of its subclasses:
#include <stdexcept>
#include <string>

class DatabaseError : public std::runtime_error {
public:
    explicit DatabaseError(const std::string& msg, int errorCode = 0)
        : std::runtime_error(msg), m_code(errorCode) {}

    int errorCode() const noexcept { return m_code; }

private:
    int m_code;
};

void connectDB(const std::string& url) {
    if (url.empty())
        throw DatabaseError("Empty connection URL", 1001);
}

int main() {
    try {
        connectDB("");
    }
    catch (const DatabaseError& e) {
        std::cerr << "DB error " << e.errorCode() << ": " << e.what() << "\n";
    }
}

The noexcept Specifier

The noexcept specifier (C++11) declares that a function will not throw any exception. If it does throw, std::terminate is called immediately. Marking move constructors and destructors noexcept enables important optimizations in the Standard Library:
class MyClass {
public:
    // noexcept move constructor: std::vector can move instead of copy on reallocation
    MyClass(MyClass&& other) noexcept
        : m_data(other.m_data), m_size(other.m_size) {
        other.m_data = nullptr;
        other.m_size = 0;
    }

    // Destructors should almost always be noexcept
    ~MyClass() noexcept { delete[] m_data; }

    // Compile-time check: noexcept if T's destructor is noexcept
    template <typename T>
    void process(T&& item) noexcept(noexcept(item.cleanup())) {
        item.cleanup();
    }

private:
    int*   m_data = nullptr;
    size_t m_size = 0;
};

// Query noexcept at compile time
static_assert(noexcept(MyClass{std::declval<MyClass>()}));
Mark destructors, move constructors, and move assignment operators noexcept by default. The Standard Library containers (like std::vector) will use move operations during reallocation only if the move constructor is noexcept-qualified, which can dramatically improve performance.

Stack Unwinding and RAII

When an exception propagates through a scope, the C++ runtime unwinds the stack: it calls the destructors of all local objects in reverse order of construction. This guarantees resource cleanup if you use RAII (Resource Acquisition Is Initialization):
#include <fstream>
#include <memory>
#include <stdexcept>

void processFile(const std::string& path) {
    std::ifstream file(path);          // RAII: file opened here
    if (!file)
        throw std::runtime_error("Cannot open: " + path);

    auto buffer = std::make_unique<char[]>(4096);  // RAII: heap memory

    // ... if an exception is thrown below, file and buffer are
    // automatically cleaned up by their destructors during unwinding

    doWork(file, buffer.get());

}   // file.close() and buffer deleted here, even if exception propagates
Do not let exceptions escape from destructors. If a destructor throws while the stack is already unwinding (due to another exception), std::terminate is called immediately. Mark destructors noexcept and handle any errors they encounter internally.

Exception Specifications (Deprecated)

The old throw(type) exception specification syntax (e.g., void f() throw(std::bad_alloc)) is deprecated in C++11 and removed in C++17. Use noexcept or noexcept(false) instead:
// Deprecated (C++03 style) — do not use:
// void f() throw(std::runtime_error);

// Modern equivalents:
void g() noexcept;          // never throws
void h() noexcept(false);   // may throw (same as no specifier)

Windows Structured Exception Handling (SEH)

Windows provides its own exception mechanism called Structured Exception Handling (SEH), which uses __try, __except, and __finally instead of try/catch. SEH can catch hardware exceptions (access violations, division by zero) that C++ exceptions cannot:
#include <Windows.h>
#include <stdio.h>

int main()
{
    __try
    {
        volatile int* pNull = nullptr;
        *pNull = 42;          // Access violation — hardware exception
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        printf("Caught SEH exception (code 0x%08X)\n",
               GetExceptionCode());
    }
    return 0;
}
The __except filter expression evaluates to one of three constants:
ConstantEffect
EXCEPTION_EXECUTE_HANDLERHandle the exception; execute the __except block
EXCEPTION_CONTINUE_SEARCHPass to the next handler up the stack
EXCEPTION_CONTINUE_EXECUTIONResume execution at the faulting instruction
The __finally block runs regardless of whether an exception occurred — similar in purpose to defer in Go or finally in Java:
void CleanupExample()
{
    HANDLE h = CreateFile(L"temp.dat", GENERIC_WRITE, 0,
                          nullptr, CREATE_ALWAYS, 0, nullptr);
    __try {
        // Write operations that might fail
        WriteData(h);
    }
    __finally {
        // Always runs — even if WriteData throws or returns early
        if (h != INVALID_HANDLE_VALUE)
            CloseHandle(h);
    }
}
Do not mix C++ exceptions and SEH in the same function. When you compile C++ code with /EHsc (the default), SEH exceptions do not trigger C++ destructors. Use /EHa if you need destructors to run for SEH exceptions, but be aware of the performance impact. For new C++ code, prefer C++ exceptions and RAII over SEH.

Comparing C++ Exceptions and SEH

FeatureC++ ExceptionsWindows SEH
StandardISO C++Microsoft/Windows only
PortabilityYesNo
Catches hardware faultsNoYes
Calls C++ destructorsYes (always)Only with /EHa
Type-safeYesNo
Recommended for C++✅ Yes❌ Use only for C code or hardware traps

See Also

Build docs developers (and LLMs) love