[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
catchby 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
| Aspect | Exceptions | Error Codes (expected, optional, bool) |
|---|---|---|
| Control flow | Clean for rare failures | Suitable for frequent, expected failures |
| Performance | Exception path is relatively expensive | Predictable in hot loops |
| C interop | Difficult to mix with C APIs | Works well with errno, return codes |
| Visibility | Not 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., whatvector::push_backguarantees 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::vectoruses move during reallocation if move constructor isnoexcept. 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
| Feature | Exceptions | Error Codes | std::expected (C++23) |
|---|---|---|---|
| Control flow | For exceptional failures | For expected failures | Explicit value-or-error |
| Performance | Slow on exception path | Predictable branching | Similar to error codes |
| Visibility | Not obvious in signature | Explicit in return | Explicit in return |
| Propagation | Automatic | Manual checking | Manual checking |
| C interop | Difficult | Easy | Easy |
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 guaranteevector::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
- Catch by reference: Avoid object slicing
- Standard hierarchy:
exception→logic_error,runtime_error - RAII: Automatic resource cleanup on exception
- Exception safety: Basic, strong, nothrow guarantees
- noexcept: For move, destructor, swap
- 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?
Related Articles
- C++ Exception Handling | try-catch-throw & Exceptions vs Error Codes
- Go Error Handling: Forget try-catch
- C++ Exception Specifications
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.