Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 예외 처리(Exception Handling)의 작동 원리와 활용 방법


C++에서 예외 처리(Exception Handling)는 프로그램 실행 중 발생하는 런타임 오류를 감지하고 대응하기 위한 메커니즘입니다. 예외(Exception)란 프로그램이 실행되는 도중에 발생하여 던져지는(throw) 이벤트를 의미하며, C++의 모든 표준 예외는 std::exception 클래스로부터 파생됩니다. 만약 예외를 적절히 처리하지 않으면 프로그램은 예외 메시지를 출력한 뒤 강제로 종료됩니다.

C++ 표준에서는 이러한 예외들을 <exception> 헤더의 클래스 형태로 정의하고 있어, 프로그램 내에서 언제든 활용할 수 있습니다. 부모-자식 관계로 이루어진 예외 클래스 계층 구조는 아래와 같습니다.

C++ 예외 처리(Exception Handling)의 작동 원리와 활용 방법

C++의 주요 표준 예외 클래스

번호예외 클래스 및 설명
1std::exception
모든 표준 C++ 예외 클래스의 부모가 되는 최상위 예외 클래스입니다.
2std::bad_cast
dynamic_cast가 실패했을 때 던져지는 예외입니다.
3std::bad_exception
프로그램에서 예상치 못한 예외가 발생했을 때 이를 처리하기 위해 사용되는 예외입니다.
4std::bad_alloc
new 연산자를 통한 동적 메모리 할당이 실패할 때 일반적으로 던져지는 예외입니다.
5std::logic_error
코드를 검토하는 것만으로도 발생 여부를 사전에 파악할 수 있는 논리적 오류에 해당하는 예외입니다.
6std::runtime_error
코드 검토만으로는 감지할 수 없고, 실제 실행 과정에서만 확인 가능한 오류에 해당하는 예외입니다.
7std::bad_typeid
typeid 연산자가 실패했을 때 던져지는 예외입니다.

예외 처리의 3대 핵심 키워드

C++의 예외 처리에는 try, catch, throw라는 세 가지 핵심 키워드가 사용됩니다.

try/catch 블록

C++에서는 try/catch 문을 통해 예외를 처리합니다. 예외가 발생할 가능성이 있는 코드는 try 블록 안에 작성하고, 실제로 예외가 발생했을 때 이를 잡아내어 처리하는 역할은 catch 블록이 담당합니다.

예제 코드

#include <iostream>
using namespace std;
class Sample1 {
    public:
        Sample1() {
            cout << "Construct an Object of sample1" << endl;
        }
        ~Sample1() {
            cout << "Destruct an Object of sample1" << endl;
        }
};
class Sample2 {
    public:
        Sample2() {
            int i=7;
            cout << "Construct an Object of sample2" << endl;
            throw i;
        }
        ~Sample2() {
            cout << "Destruct an Object of sample2" << endl;
        }
};
int main() {
    try {
        Sample1 s1;
        Sample2 s2;
    } catch(int i) {
            cout << "Caught " << i << endl;
    }
}

실행 결과

Construct an Object of sample1
Construct an Object of sample2
Destruct an Object of sample1
Caught 7

위 예제에서 Sample2 객체의 생성자가 호출되던 도중 예외가 던져졌습니다. 이때 스택 풀기(Stack Unwinding) 과정이 진행되면서 이미 생성이 완료된 s1 객체의 소멸자가 자동으로 호출되는 것을 확인할 수 있습니다. 반면, 생성이 완료되지 않은 s2의 소멸자는 호출되지 않습니다.

사용자 정의 예외(User Defined Exception)

표준 exception 클래스를 상속받고 필요한 기능을 재정의(오버라이딩)하면, 개발자가 직접 자신만의 예외 클래스를 정의할 수도 있습니다.

예제 코드

#include <iostream>
#include <exception>
using namespace std;
struct DivideByZero : public exception {
    const char * what () const throw () {
        return "My Exception";
    }
};
int main() {
    try {
        throw DivideByZero();
    } catch(DivideByZero& e) {
        cout << "Exception caught" << endl;
        cout << e.what() << endl;
    } catch(exception& e) {
    }
}

실행 결과

Exception caught
My Exception

what() 메서드란?

what()exception 클래스가 제공하는 public 메서드로, 모든 하위 예외 클래스에서 재정의할 수 있습니다. 이 메서드는 예외가 발생한 원인을 설명하는 문자열을 반환하며, 위 예제에서는 DivideByZero 클래스에서 이를 오버라이딩하여 "My Exception"이라는 메시지를 반환하도록 구현했습니다.

참고로 const throw() 같은 동적 예외 명세는 C++11 이후 폐기(deprecated)되었으므로, 현대 C++에서는 noexcept 키워드를 사용하는 것이 권장됩니다.