C++ 예외(Exception) 처리
개요
C++ 예외 처리는 프로그램 실행 중에 발생하는 오류나 예외적인 상황(예: 0으로 나누기, 파일 열기 실패 등)을 처리하기 위한 강력하고 유연한 메커니즘입니다. 예외가 발생하면 일반적인 프로그램 흐름이 중단되고, 해당 예외를 처리할 수 있는 catch
블록으로 제어가 이동합니다. 이를 통해 오류 처리 코드를 기본 로직과 분리하여 코드의 가독성과 유지보수성을 높일 수 있습니다.
C++ 버전별 주요 키워드 도입 시기
- C++98:
try
,catch
,throw
키워드와std::exception
클래스가 표준으로 도입되었습니다. 기본적인 예외 처리의 틀이 완성되었습니다. - C++11:
noexcept
지정자가 도입되었습니다. 함수가 예외를 발생시키지 않는다는 것을 명시적으로 선언하여 컴파일러가 최적화를 수행할 수 있도록 돕습니다.
내용 설명
C++ 예외 처리는 세 가지 주요 키워드로 구성됩니다.
throw
: 예외가 발생했음을 알리는 데 사용됩니다.throw
표현식은 예외 객체를 생성하고 던집니다. 던져진 객체는 어떤 타입이든 될 수 있지만, 일반적으로std::exception
을 상속하는 클래스의 객체를 사용하는 것이 좋습니다.try
: 예외가 발생할 가능성이 있는 코드를 감싸는 블록입니다.try
블록 내에서 예외가 발생하면, 프로그램은 즉시 실행을 멈추고 해당 예외를 처리할 수 있는catch
블록을 찾습니다.catch
:try
블록에서 발생한 예외를 처리하는 코드 블록입니다.catch
는 특정 타입의 예외를 받아서 처리할 수 있으며, 여러 개의catch
블록을 사용하여 다양한 타입의 예외를 처리할 수 있습니다. 예외를 잡을 때는 값보다는const
참조(const T&
)로 받는 것이 효율적입니다.noexcept
(C++11): 함수가 예외를 던지지 않음을 명시합니다. 만약noexcept
로 선언된 함수에서 예외가 발생하면, 예외 처리 메커니즘을 통하지 않고 즉시std::terminate
가 호출되어 프로그램이 종료됩니다. 이는 컴파일러에게 최적화 기회를 제공합니다.
예제 코드
사용자로부터 입력받은 값으로 나눗셈을 수행하며, 0으로 나눌 경우 std::runtime_error
예외를 던지는 예제입니다.
#include <iostream>
#include <stdexcept> // std::runtime_error를 사용하기 위해 필요
// noexcept(false)는 이 함수가 예외를 던질 수 있음을 명시합니다. (기본값이므로 생략 가능)
double div(double x, double y) noexcept(false)
{
if (y == 0.0)
{
// C-스타일 문자열 대신 std::exception의 파생 클래스를 사용하는 것이 좋습니다.
throw std::runtime_error("Division by zero!");
}
return x / y;
}
int main()
{
double x = 10.0;
double y;
std::cout << "We will compute (x/y)" << '\n';
std::cout << "Please enter a value for y= ";
std::cin >> y;
std::cout << '\n';
try
{
double z = div(x, y);
std::cout << "x/y = " << z << '\n';
}
// 예외를 const 참조로 받는 것이 일반적입니다.
catch (const std::runtime_error& e)
{
// e.what() 멤버 함수로 오류 메시지를 얻을 수 있습니다.
std::cerr << "Error: " << e.what() << '\n';
}
return 0;
}
실행 결과
정상 실행 (y = 5 입력 시)
We will compute (x/y)
Please enter a value for y= 5
x/y = 2
예외 발생 (y = 0 입력 시)
We will compute (x/y)
Please enter a value for y= 0
Error: Division by zero!
활용 팁
- 예외는 값으로 던지고, 참조로 받으세요 (
throw by value, catch by reference
): 예외 객체를const
참조로 받으면 불필요한 복사를 피하고, 다형성(Polymorphism)을 활용하여 부모 클래스 타입으로 자식 클래스 예외를 받을 수 있습니다. - RAII(Resource Acquisition Is Initialization)를 활용하세요: 예외가 발생하면 스택이 풀리면서 지역 변수들의 소멸자가 호출됩니다. 스마트 포인터(
std::unique_ptr
,std::shared_ptr
)나std::lock_guard
와 같은 RAII 객체를 사용하면 예외 발생 시에도 자원이 안전하게 해제되도록 보장할 수 있습니다. - 소멸자에서는 예외를 던지지 마세요: 소멸자에서 예외를 던지는 것은 매우 위험합니다. 스택 풀기(stack unwinding) 과정에서 다른 예외를 처리하던 중 소멸자가 예외를 던지면
std::terminate
가 호출되어 프로그램이 즉시 종료됩니다. 소멸자는noexcept(true)
로 간주하는 것이 좋습니다. - 예외는 정말 '예외적인' 상황에만 사용하세요: 예외 처리는 비용이 따르므로, 일반적인 프로그램 제어 흐름(if-else 등)으로 처리할 수 있는 로직에 남용해서는 안 됩니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : std::async (11) (1) | 2025.10.21 |
---|---|
Modern C++ : std::scoped_lock (17) (0) | 2025.10.20 |
Modern C++ : std::shared_timed_mutex (14) (0) | 2025.10.19 |
Modern C++ : std::mutex (11, 14, 17) (1) | 2025.10.18 |
Modern C++ : std::thread (11) (0) | 2025.10.17 |
Modern C++ : std::exception (98, 11, 17) (0) | 2025.10.16 |
Modern C++ : std::weak_ptr (11) (1) | 2025.10.15 |
Modern C++ : std::shared_ptr (11, 17) (0) | 2025.10.14 |