스토리텔링 개발자

[More Effective C++] 11. 소멸자 예외 처리 본문

개발/More Effective C++

[More Effective C++] 11. 소멸자 예외 처리

김디트 2024. 8. 13. 10:09
728x90

항목 11 : 소멸자에서는 예외가 탈출하지 못하게 하자

 

 

 

소멸자가 호출되는 상황
  1. 객체가 통상적인 조건에서 소멸되었을 때
    • 지역변수 객체가 유효범위(scope)를 벗어날 때
    • 객체가 직접 삭제(delete)될 때
  2. 예외 처리 매커니즘에 의해 객체가 소멸되었을 때
    • 예외 전파(exception propagation) 과정의 일부분으로 스택 되감기가 진행될 때
  • 즉, 소멸자가 호출되었을 때 예외가 발생된 상태인지 아닌지 알 방도가 없다.
    • 실은 이제는 std::uncaught_exception을 통해서 예외가 스택 되감기(unwinding) 중인지 알아낼 수 있다.

 

 

 

예외가 소멸자를 빠져나가면 안되는 이유 1
  • 소멸자 내부에서 예외 발생 상태인지 구별할 방법이 없다.
    • 어떤 상황이든 예외가 발생된 상태라고 가정하고 소멸자를 방어적으로 작성하도록 한다.
    • 만일 예외가 중첩되면 C++이 terminate를 호출하여 프로그램을 강제 종료해 버린다.
class Session
{
public:
    Session();
    ~Session();
    ...
private:
    static void logCreation(Session* objAddr);
    static void logDestruction(Session* objAddr);
};

Session::~Session()
{
    logDestruction(this); // 만약 여기서 예외가 발생한다면?
    // 여기서 try / catch로 처리하지 않기 때문에 예외는 빠져나간다.
    
    // 하지만 이 객체의 소멸자가 호출된 원래 이유가 또 다른 예외 때문이었다면?
    // C++는 바로 terminate를 호출하고, 프로그램이 죽어버린다.
}
  • try-catch 블록으로 소멸자에서 발생한 예외가 빠져나가지 않게 한다.
Session::~Session()
{
    try
    {
        logDestruction(this);
    }
    catch( ... )
    {
        cerr << "Unable to log destruction of Session object "
             << "at address "
             << this
             << ".\n";
         // 하지만...
         // operator<<에서 예외가 발생한다면?
         // 역시나 예외가 소멸자가 빠져나간다..
     }
 }
  • try-catch 문을 또 안에? 구조가 너무 복잡해진다..
  • 그러니 그냥 예외가 발생하면 로깅 처리를 하지 않도록 한다.
Session::~Session()
{
    try
    {
        logDestruction(this);
    }
    catch( ... ) {} // 아무 일도 하지 않는다.
}
  • 아무 일도 하지 않는 것이 아니라..
    • 실은 logDestruction에서 발생한 예외가 밖으로 전파되지 않도록 잡아두는 역할이다.
    • 예외가 소멸자를 못 빠져나가게 한다는 목적에는 이것으로 충분하다.

 

 

 

예외가 소멸자를 빠져나가면 안되는 이유 2
  • 소멸자에서 발생된 예외가 소멸자에서 처리되지 않으면
    • 소멸자는 실행이 끝나지 않은 상태로 남게 된다.
Session::Session()
{
    logCreation(this);
    startTransaction();
}

Session::~Session()
{
    logDestruction(this); // 여기서 예외가 발생하고, 빠져나가면...
    endTransaction(); // endTransaction은 영원히 호출되지 않는 상황이!!
}
  • endTransaction과 logDestruction의 순서를 바꾼다?
    • endTransaction에서도 예외가 발생할 수 있으므로
    • transaction 정리가 마무리되지 않을 수 있는 건 마찬가지이다.

 

 

 

정리
  1. 예외가 전파되던 중 소멸자가 불렸고, 그 와중에 소멸자가 다른 예외를 던지면 프로그램이 멈춘다.
    • 소멸자가 예외를 토해내면 예외가 중첩되어 terminate 함수가 호출되기 때문이다.
    • 그러므로 소멸자 안에서 예외를 소화해 주어야 한다.
  2. 소멸자의 동작이 완전히 끝나지 않는다.
    • 소멸자에서 예외가 빠져나가면 그 소멸자는 실행이 끝나지 않은 상태로 남는다.
    • 그러므로 소멸자 안에서 예외를 처리하고 소멸자가 해야 할 일을 온전히 마무리 지어주어야 한다.

 

 

 

참조
 

[Effective C++] 8. 소멸자 호출 중 예외 문제

항목 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자.   C++은 예외를 내보내는 소멸자를 좋아하지 않는다.'소멸자에서 예외 발생'은 미정의 사항이다.(컴파일러 구현에 따라 다르게 반응한다

delightlane.tistory.com

 

728x90
Comments