[2026] C++ Exception Handling Complete Guide | try/catch/throw & RAII Patterns

[2026] C++ Exception Handling Complete Guide | try/catch/throw & RAII Patterns

이 글의 핵심

Master C++ exceptions: standard hierarchy, catch-by-reference, exception safety guarantees, RAII patterns, noexcept, exceptions vs error codes, and production patterns.

Basic Exception Handling

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <stdexcept>
using namespace std;
int divide(int a, int b) {
    if (b == 0) {
        throw runtime_error("Cannot divide by zero");
    }
    return a / b;
}
int main() {
    try {
        int result = divide(10, 0);
        cout << result << endl;
    } catch (runtime_error& e) {
        cout << "Error: " << e.what() << endl;
    }
    
    return 0;
}

Output:

Error: Cannot divide by zero

Standard Exception Hierarchy

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <exception>
#include <stdexcept>
// Base exception
exception
// Logic errors
logic_error
  ├─ invalid_argument
  ├─ domain_error
  ├─ length_error
  └─ out_of_range
// Runtime errors
runtime_error
  ├─ range_error
  ├─ overflow_error
  └─ underflow_error

Hierarchy visualization: 아래 코드는 mermaid를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

graph TD
    A[std::exception] --> B[std::logic_error]
    A --> C[std::runtime_error]
    B --> D[std::invalid_argument]
    B --> E[std::out_of_range]
    B --> F[std::length_error]
    C --> G[std::range_error]
    C --> H[std::overflow_error]
    
    style A fill:#FFB6C1
    style B fill:#87CEEB
    style C fill:#90EE90

Multiple Exception Handling

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

try {
    // Code that may throw
} catch (out_of_range& e) {
    cout << "Out of range: " << e.what() << endl;
} catch (invalid_argument& e) {
    cout << "Invalid argument: " << e.what() << endl;
} catch (exception& e) {
    cout << "Other error: " << e.what() << endl;
} catch (...) {
    cout << "Unknown error" << endl;
}

Catch order matters: Catch specific types first, then base types. Otherwise, base type catches everything and specific handlers never execute.

Practical Examples

Example 1: File Processing Exception

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
string readFile(const string& filename) {
    ifstream file(filename);
    
    if (!file.is_open()) {
        throw runtime_error("Cannot open file: " + filename);
    }
    
    string content, line;
    while (getline(file, line)) {
        content += line + "\n";
    }
    
    return content;
}
int main() {
    try {
        string content = readFile("config.txt");
        cout << content << endl;
    } catch (runtime_error& e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }
    
    return 0;
}

Explanation: Throwing exceptions for file errors makes error handling explicit and propagates failures clearly.

Example 2: Custom Exception Class

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <exception>
#include <string>
using namespace std;
class InvalidAgeException : public exception {
private:
    string message;
    
public:
    InvalidAgeException(int age) {
        message = "Invalid age: " + to_string(age);
    }
    
    const char* what() const noexcept override {
        return message.c_str();
    }
};
class Person {
private:
    string name;
    int age;
    
public:
    Person(string n, int a) : name(n) {
        if (a < 0 || a > 150) {
            throw InvalidAgeException(a);
        }
        age = a;
    }
    
    void print() {
        cout << name << ", " << age << " years old" << endl;
    }
};
int main() {
    try {
        Person p1("Alice", 25);
        p1.print();
        
        Person p2("Bob", 200);  // Throws exception
        p2.print();
    } catch (InvalidAgeException& e) {
        cerr << "Error: " << e.what() << endl;
    }
    
    return 0;
}

Output:

Alice, 25 years old
Error: Invalid age: 200

Explanation: Domain-specific exceptions make errors more expressive and easier to handle.

Example 3: RAII and Exception Safety

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
    Resource() { cout << "Resource allocated" << endl; }
    ~Resource() { cout << "Resource released" << endl; }
    void use() { cout << "Resource used" << endl; }
};
void dangerousFunction() {
    throw runtime_error("Error occurred!");
}
int main() {
    try {
        // ❌ Manual management (leaks on exception)
        // Resource* r = new Resource();
        // dangerousFunction();
        // delete r;  // Never executed!
        
        // ✅ RAII (automatic cleanup)
        unique_ptr<Resource> r = make_unique<Resource>();
        r->use();
        dangerousFunction();
        // Automatically released even on exception
        
    } catch (exception& e) {
        cout << "Error: " << e.what() << endl;
    }
    
    return 0;
}

Output: 다음은 간단한 code 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

Resource allocated
Resource used
Resource released
Error: Error occurred!

Explanation: Smart pointers ensure resources are safely released even when exceptions occur.

Common Issues

Issue 1: Catching by Value

Symptom: Exception object slicing Cause: Catching by value loses derived class information Solution: 다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ Wrong: catch by value
try {
    throw runtime_error("error");
} catch (exception e) {  // Catch by value
    // Derived class information lost
}
// ✅ Correct: catch by reference
try {
    throw runtime_error("error");
} catch (exception& e) {  // Catch by reference
    cout << e.what() << endl;
}
// ✅ const reference (safer)
catch (const exception& e) {
    cout << e.what() << endl;
}

Key: Always catch by reference (const exception&) to avoid object slicing and preserve polymorphic behavior.

Issue 2: Throwing from Destructor

Symptom: Program abnormal termination Cause: Exception from destructor during stack unwinding calls terminate() Solution: 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ Dangerous
class Resource {
public:
    ~Resource() {
        throw runtime_error("error");  // Never do this!
    }
};
// ✅ Correct
class Resource {
public:
    ~Resource() noexcept {
        try {
            // Risky operation
        } catch (...) {
            // Swallow exception
        }
    }
};

Issue 3: Exception Specification

Symptom: Warning or error when using throw() Cause: throw() is deprecated in C++11 Solution: 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ Old style (deprecated)
void func() throw(int, runtime_error) {
    // ...
}
// ✅ Use noexcept
void func() noexcept {  // Doesn't throw
    // ...
}
void func2() noexcept(false) {  // May throw
    // ...
}

try-catch Basics Summary

  • try: Code block that may throw exceptions.
  • throw: Thrown type is matched to first matching catch by type. Catch derived classes before base classes to avoid slicing.
  • catch (…): Catches any type; usually placed last. Log before rethrowing to avoid silent swallow. 아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
try {
    mayThrow();
} catch (const std::invalid_argument& e) {
    // Specific handling
} catch (const std::exception& e) {
    // Common standard exception handling
} catch (...) {
    // Unknown exceptions
}

Catch by reference: catch (const std::exception& e) is safer and more efficient than catching by value.

Exceptions vs Error Codes

AspectExceptionsError Codes (expected, optional, bool)
Control flowClean for rare failuresSuitable for frequent, expected failures
PerformanceException path is relatively expensivePredictable in hot loops
C interopDifficult to mix with C APIsWorks well with errno, return codes
VisibilityNot always obvious from signature (pre-C++17)Explicit in return type
C++23 std::expected<T, E>: Models “value or error” explicitly, enabling failure propagation without exceptions. Team convention of “exceptions only for truly exceptional failures” reduces confusion.

RAII and Exception Safety

RAII: Resources acquired in constructor, released in destructor. Even when exceptions occur, stack unwinding calls destructors, preventing leaks.

Exception Safety Guarantees

  • Basic guarantee: No resource leaks; invariants may be temporarily broken but object remains valid.
  • Strong guarantee: Commit-or-rollback (transaction-like; copy-and-swap idiom).
  • Nothrow guarantee: Operation doesn’t throw exceptions (expressible with noexcept). In production, knowing standard container exception guarantees (e.g., what vector::push_back guarantees on failure) simplifies design. For custom classes, document what happens to class invariants when copy/move assignment throws.

noexcept Deep Dive

  • Meaning: “This function doesn’t throw exceptions out.” Violation typically leads to std::terminate.
  • Move operations: std::vector uses move during reallocation if move constructor is noexcept. Adding noexcept to move constructor/assignment directly impacts performance.
  • Destructor: Destructors are implicitly noexcept. Never throw from destructors.
void swap(MyType& a, MyType& b) noexcept {
    // Only calls member swap, guarantees no exceptions
}

Conditional noexcept: noexcept(expr) form allows conditional specification.

Production Patterns

Pattern 1: Constructor Failure

Constructors have no return value, so for invalid state, throw exception (or use factory + expected). 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

class Database {
public:
    Database(const string& connStr) {
        if (!connect(connStr)) {
            throw runtime_error("Connection failed");
        }
    }
};

Pattern 2: Catch Only at Boundaries

Library internals propagate exceptions; catch at UI/main for logging/recovery. 다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Library code: propagate
void processData() {
    if (error) throw DataException("...");
}
// Application boundary: catch
int main() {
    try {
        processData();
    } catch (const exception& e) {
        log(e.what());
        return 1;
    }
    return 0;
}

Pattern 3: std::nested_exception

Wrap low-level exceptions to preserve cause when propagating upward. 다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <exception>
#include <stdexcept>
#include <iostream>
void lowLevel() {
    throw std::runtime_error("Low-level error");
}
void midLevel() {
    try {
        lowLevel();
    } catch (...) {
        std::throw_with_nested(std::runtime_error("Mid-level error"));
    }
}
int main() {
    try {
        midLevel();
    } catch (const std::exception& e) {
        std::cout << e.what() << std::endl;
        try {
            std::rethrow_if_nested(e);
        } catch (const std::exception& nested) {
            std::cout << "  Caused by: " << nested.what() << std::endl;
        }
    }
}

Pattern 4: Testing Exceptions

Use test frameworks like Catch2 to verify exception types. 다음은 간단한 cpp 코드 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Catch2 example
TEST_CASE("Division by zero throws") {
    REQUIRE_THROWS_AS(divide(10, 0), std::runtime_error);
}

Pattern 5: Team Convention

Establish team convention: hot loops use error codes, I/O/parsing failures use exceptions.

Best Practices

1. Catch by const Reference

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ✅ Best
catch (const std::exception& e) {
    // ...
}
// ❌ Avoid: catch by value (slicing)
catch (std::exception e) {
    // ...
}

2. Order Specific to General

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

try {
    // ...
} catch (const std::out_of_range& e) {
    // Specific
} catch (const std::logic_error& e) {
    // More general
} catch (const std::exception& e) {
    // Most general
} catch (...) {
    // Unknown
}

3. Don’t Swallow Silently

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ Silent swallow
catch (...) {
    // Nothing—hides errors!
}
// ✅ Log and rethrow
catch (...) {
    log("Unknown exception");
    throw;  // Rethrow
}

4. Use RAII for Resources

// ✅ Smart pointers, RAII wrappers
auto file = std::make_unique<FileHandle>("data.txt");
// Automatically closed even on exception

5. noexcept for Move and Destructor

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

class MyClass {
public:
    MyClass(MyClass&&) noexcept;
    ~MyClass() noexcept;
};

Exception Safety Guarantees

Basic Guarantee

No resource leaks; object remains in valid (but potentially unspecified) state. 다음은 간단한 cpp 코드 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

void push_back(const T& value) {
    // If exception during reallocation, no leaks
    // but vector state may change
}

Strong Guarantee

Commit-or-rollback: operation succeeds completely or leaves state unchanged. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Copy-and-swap idiom
T& operator=(const T& other) {
    T temp(other);  // Copy
    swap(*this, temp);  // Swap (noexcept)
    return *this;
    // If copy throws, *this unchanged
}

Nothrow Guarantee

Operation never throws exceptions.

void swap(T& a, T& b) noexcept {
    // Guaranteed no exceptions
}

Exceptions vs Error Codes vs expected

Comparison Table

FeatureExceptionsError Codesstd::expected (C++23)
Control flowFor exceptional failuresFor expected failuresExplicit value-or-error
PerformanceSlow on exception pathPredictable branchingSimilar to error codes
VisibilityNot obvious in signatureExplicit in returnExplicit in return
PropagationAutomaticManual checkingManual checking
C interopDifficultEasyEasy

When to Use Each

Exceptions:

  • Rare, unexpected failures
  • Constructor failures (no return value)
  • Deep call stacks needing error propagation
  • I/O, parsing, validation errors Error Codes:
  • Frequent, expected failures (e.g., “not found”)
  • Performance-critical hot loops
  • C API interop
  • When failure is part of normal flow std::expected (C++23):
  • Explicit value-or-error modeling
  • When you want error code benefits with type safety
  • API boundaries where failure is common
// C++23 std::expected example
#include <expected>
std::expected<int, std::string> divide(int a, int b) {
    if (b == 0) {
        return std::unexpected("Division by zero");
    }
    return a / b;
}
int main() {
    auto result = divide(10, 0);
    if (result) {
        std::cout << "Result: " << *result << "\n";
    } else {
        std::cout << "Error: " << result.error() << "\n";
    }
}

RAII and Exception Safety

RAII (Resource Acquisition Is Initialization): Acquire resources in constructor, release in destructor. Stack unwinding during exception propagation calls destructors, preventing leaks.

RAII Example

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <fstream>
class FileGuard {
    std::FILE* file_;
public:
    FileGuard(const char* path) : file_(std::fopen(path, "r")) {
        if (!file_) throw std::runtime_error("Cannot open file");
    }
    ~FileGuard() noexcept {
        if (file_) std::fclose(file_);
    }
    std::FILE* get() { return file_; }
};
void processFile(const char* path) {
    FileGuard file(path);  // RAII: auto-closes on exception
    // Use file.get()...
    // Even if exception occurs, destructor closes file
}

Exception Safety in Practice

Knowing standard container guarantees helps design:

  • vector::push_back: Strong guarantee (if reallocation fails, vector unchanged)
  • vector::reserve: Strong guarantee
  • vector::insert: Basic guarantee (may partially modify) For custom classes, document exception guarantees in copy/move operations.

Production Patterns

Pattern 1: Boundary Exception Handler

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <exception>
int main() {
    try {
        // Application logic
        runApplication();
    } catch (const std::exception& e) {
        std::cerr << "Fatal error: " << e.what() << "\n";
        logError(e);
        return 1;
    } catch (...) {
        std::cerr << "Unknown fatal error\n";
        return 2;
    }
    return 0;
}

Pattern 2: Exception Translation

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Translate low-level exceptions to domain exceptions
void apiCall() {
    try {
        lowLevelOperation();
    } catch (const std::system_error& e) {
        throw DatabaseException("Database connection failed", e);
    }
}

Pattern 3: Scope Guard

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <functional>
class ScopeGuard {
    std::function<void()> cleanup_;
    bool dismissed_ = false;
public:
    ScopeGuard(std::function<void()> f) : cleanup_(std::move(f)) {}
    ~ScopeGuard() noexcept {
        if (!dismissed_) {
            try {
                cleanup_();
            } catch (...) {
                // Log but don't rethrow
            }
        }
    }
    void dismiss() { dismissed_ = true; }
};
// Usage
void transactionalOperation() {
    beginTransaction();
    ScopeGuard guard([]() { rollback(); });
    
    // Operations...
    
    commit();
    guard.dismiss();  // Success: don't rollback
}

FAQ

Q1: Are exceptions slow?

A: If no exception is thrown, overhead is minimal. Exceptions are slow only when thrown and caught. For normal flow, performance impact is negligible.

Q2: When to use exceptions?

A:

  • Unrecoverable errors
  • Constructor failures
  • Deep call stack error propagation When NOT to use:
  • Normal control flow
  • Performance-critical loops

Q3: return vs throw?

A:

  • return: Normal termination, expected result
  • throw: Abnormal situation, error

Q4: Must I catch all exceptions?

A: Catch only exceptions you can handle. If you can’t handle, let it propagate upward. Catching everything and swallowing hides problems.

Q5: Why use noexcept?

A:

  • Enables compiler optimizations
  • Essential for move constructors (container optimization)
  • Clarifies intent

Q6: Exceptions vs error codes?

A:

  • Exceptions: Cleaner code, easier error propagation
  • Error codes: Performance-critical, C compatibility needed

Summary

Key Points

  1. Catch by reference: Avoid object slicing
  2. Standard hierarchy: exceptionlogic_error, runtime_error
  3. RAII: Automatic resource cleanup on exception
  4. Exception safety: Basic, strong, nothrow guarantees
  5. noexcept: For move, destructor, swap
  6. Exceptions vs codes: Choose based on failure frequency and performance needs

Exception Handling Checklist

  • Catching by const reference?
  • Specific exceptions before general?
  • RAII for all resources?
  • Destructors don’t throw?
  • Move operations marked noexcept?
  • Logging before swallowing exceptions?

Keywords

C++ exceptions, try-catch, throw, RAII, exception safety, noexcept, error handling One-line summary: C++ exceptions provide clean error propagation with RAII for automatic resource cleanup. Use for rare failures, error codes for frequent failures, and always catch by reference.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3