본문 바로가기
프로그래밍/C++

C++ Structured Exception Handling (구조화된 예외 처리)

by 머니테크리더 2023. 10. 3.
반응형

C++ Structured Exception Handling (구조화된 예외 처리)
C++ Structured Exception Handling (구조화된 예외 처리)

 

🔖 INDEX

     

     

    기본 설명

    구조화된 예외 처리는 코드 내에서 예외적인 상황을 안전하고 효과적으로 처리하기 위한 프로그래밍 패러다임입니다. C++에서의 구조화된 예외 처리는 주로 try, catch, throw 키워드와 함께 사용되는 관련 메커니즘을 기반으로 합니다.

     

    throw

    • 기능: 프로그램의 실행 중 예외적인 상황을 발견할 때 예외를 발생(던지기)시키는 데 사용됩니다.
    • 사용: throw 키워드 뒤에는 발생시킬 예외 객체를 지정합니다.
    throw std::runtime_error("This is an error message.");

     

    try

    • 기능: 예외가 발생할 가능성이 있는 코드 블록을 감쌉니다.
    • 사용: try 블록 내에서 발생하는 모든 예외는 해당 블록 뒤에 오는 catch 블록에서 처리됩니다.
    try {
        // 예외 발생 가능성이 있는 코드
        throw "Some exception";
    }

     

    catch

    • 기능: try 블록 내에서 발생한 특정 타입의 예외를 잡아내어 처리하는 데 사용됩니다.
    • 사용: catch 키워드 뒤에는 처리할 예외의 타입을 지정합니다. 여러 catch 블록을 연속해서 배치할 수 있으며, 가장 먼저 일치하는 타입의 catch 블록이 실행됩니다. 특정 타입을 지정하지 않고 모든 예외를 잡기 위해 catch(...)를 사용할 수도 있습니다.
    catch (const std::exception& e) {
        // std::exception 타입의 예외 처리
        std::cerr << e.what() << std::endl;
    }
    catch (...) {
        // 알 수 없는 타입의 예외 처리
        std::cerr << "Unknown exception caught!" << std::endl;
    }

     

     

    사용 예제

    다양한 예외 타입 처리

    여러 catch 블록을 사용하여 다양한 타입의 예외를 처리할 수 있습니다.

    try {
        // ...
    }
    catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    }
    catch (const std::out_of_range& e) {
        std::cerr << "Out of range error: " << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << "Unknown exception caught" << std::endl;
    }

     

    사용자 정의 예외

    std::exception을 상속받아 사용자 정의 예외를 만들 수 있습니다.

    class MyException : public std::exception {
    public:
        const char* what() const noexcept override {
            return "My custom exception";
        }
    };
    
    // 사용 예제
    try {
        throw MyException();
    }
    catch (const MyException& e) {
        std::cerr << e.what() << std::endl;
    }

     

    함수에서의 예외 전파

    함수 내에서 발생한 예외는 호출자에게 전파될 수 있습니다.

    void mightThrowException(bool condition) {
        if (condition) {
            throw std::runtime_error("Error in function");
        }
    }
    
    int main() {
        try {
            mightThrowException(true);
        }
        catch (const std::exception& e) {
            std::cerr << "Caught in main: " << e.what() << std::endl;
        }
    }

     

    예외 재던지기

    catch 블록 내에서 throw; 문장을 사용하여 현재 처리 중인 예외를 다시 던질 수 있습니다. 이는 더 상위 레벨의 처리기에서 예외를 처리하려는 경우 유용합니다.

    try {
        // ...
    }
    catch (const std::exception& e) {
        // Do some logging or cleanup
        throw; // Re-throw the exception
    }

     

     

    주요 특징

    • 타입 안전: catch 블록에서는 해당 타입의 예외만 처리하며, 다른 타입의 예외는 무시합니다.
    • 스택 언와인딩: 예외가 발생하면 현재 함수의 스택 프레임이 언와인드(제거)되고, 호출자 함수로 돌아갑니다. 이 과정은 catch 블록을 찾을 때까지 계속됩니다.
    • 복수의 catch 블록: 여러 catch 블록을 사용하여 다양한 타입의 예외를 처리할 수 있습니다.
    • std::exception: C++ 표준 라이브러리는 std::exception을 기본 클래스로 하는 여러 예외 클래스를 제공합니다.

     

    참고 사항

    예외 안전성 (Exception Safety)

    • 예외 안전성은 코드가 예외 상황에서도 올바르게 동작하는 것을 의미합니다. 다음과 같은 여러 단계의 안전성이 있습니다:
      • No-throw guarantee: 함수가 절대로 예외를 던지지 않음을 보장.
      • Strong exception guarantee: 함수가 예외를 던지면, 프로그램 상태는 함수 호출 이전 상태로 돌아갑니다.
      • Basic exception guarantee: 예외가 발생해도 프로그램의 내부 상태는 유효한 상태를 유지합니다. 하지만 호출 이전 상태로 되돌린다는 보장은 없습니다.
    • 예외 안전성은 특히 컨테이너, 알고리즘, 리소스 관리 클래스를 설계할 때 중요합니다.

     

    예외 명세 (Exception Specifications)

    • C++11 이전에는 throw 키워드를 사용하여 함수가 던질 수 있는 예외의 타입을 지정할 수 있었습니다. 하지만 이 기능은 예상대로 동작하지 않아 종종 문제가 되었으며, C++11에서는 deprecated 되었습니다.
    • C++11에서는 noexcept 키워드가 도입되었고, 해당 함수가 예외를 던지지 않는다는 것을 명시하는데 사용됩니다.

     

    리소스 관리 (Resource Management)

    • C++에서는 "Resource Acquisition Is Initialization (RAII)" 패턴이 자주 사용됩니다. 이 패턴은 리소스 (메모리, 파일, 네트워크 연결 등) 할당과 초기화를 객체의 생성과 연결하여, 객체의 소멸자에서 리소스를 해제합니다. 이러한 접근 방식은 예외가 발생할 때 리소스 누수를 방지하는 데 효과적입니다.

     

    표준 예외 계층 구조

    • C++ 표준 라이브러리는 std::exception을 기반으로 한 다양한 예외 클래스들을 제공합니다. 이를 알고 있으면 특정한 예외 상황에 맞는 예외 타입을 던질 수 있으며, 더 구체적인 예외 처리를 할 수 있습니다.

     

    Stack Unwinding (스택 언와인딩)

    Stack Unwinding (스택 언와인딩)은 C++에서 예외가 발생했을 때 수행되는 프로세스를 의미합니다. 예외가 발생하면, 프로그램은 적절한 예외 처리기(catch 블록)를 찾을 때까지 현재 스택 프레임에서 이전 스택 프레임으로 돌아가며(즉, "언와인딩"한다) 이 과정에서 각 스택 프레임에 있는 객체들의 소멸자가 호출됩니다.

    스택 언와인딩의 주요 특징 및 과정은 다음과 같습니다:

    • 예외 발생: throw 표현식에 의해 예외가 발생합니다.
    • 현재 스택 프레임 탐색: 현재 함수 내에서 해당 예외를 처리할 catch 블록이 있는지 확인합니다.
    • 소멸자 호출: 현재 스택 프레임에서 생성된 모든 지역 객체의 소멸자가 호출됩니다.
    • 스택 프레임 이동: 적절한 catch 블록을 찾지 못한 경우, 프로그램은 호출 스택의 이전 프레임으로 이동합니다.
    • 반복: 이 프로세스는 적절한 catch 블록을 찾거나, 전체 호출 스택을 다 돌았지만 아무런 catch 블록도 찾지 못할 때까지 반복됩니다.
    • 처리되지 않은 예외: 스택 전체를 언와인딩한 후에도 적절한 catch 블록을 찾지 못하면, std::terminate() 함수가 호출됩니다.

    스택 언와인딩의 주요 이점은 자원 관리와 연관되어 있습니다. 예외가 발생하면 프로그램은 스택을 거슬러 올라가며 각 스택 프레임의 지역 객체들의 소멸자를 호출하므로, 객체들이 소유하고 있던 리소스(예: 동적 메모리, 파일 핸들 등)는 안전하게 해제됩니다. 이것은 C++의 RAII(Resource Acquisition Is Initialization) 패턴의 핵심적인 부분이며, 이로 인해 예외 상황에서도 리소스 누수를 방지할 수 있습니다.

     

     

    댓글