페이지

2015년 1월 4일 일요일

예외가 소멸자를 떠나지 못하도록 붙들어 놓자.

* 소멸자에서 예외가 빠져나가면 안 됩니다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야 합니다.

class DBConnection {
public:
    static DBCpnnection create();
    void close();
    ...
};

위의 예제에서 close()를 직접 호출해야만 하는 설계이다. 사용자의 망각을 사전에 차단하는 좋은 방법은 DBConnection 의 자원 관리 Class를 만들어서 그 Class의 소멸자에서 close()를 호출하는 것이다.

class DBConn {
public:
    ...
    ~DBConn() { db.close(); }
private:
    DBConnection db;
};

하지만 close()에서 예외를 발생시키면 소멸자는 이 예외를 전파할 것이다. 예외가 밖으로 나가도록 내버려 둔다는 것은 곧 '걱정거리'를 의미한다. 이 걱정 거리를 피하는 방법은 두가지 이다.

첫째, 프로그램을 바로 끝낸다. 보통 abort()를 호출해서, 못 볼꼴을 미리 안 보여 주겠다는 의도이다.

DBConn::~DbConn()
{
    try { db.close(); }
    catch (...) { std::abort(); }
}

둘째, 예외를 삼켜 버린다. 단 '예외 삼키기'를 선택한 것이 제대로 빛을 보려면, 발생한 예외를 그냥 무시한 뒤라도 프로그램이 신뢰성 있게 실행을 지속할 수 있어야 한다.

DBConn::~DbConn()
{
    try { db.close(); }
    catch (...) { }
}

다른 좋은 방법은 없을까 ? 예외 처리를 소멸자가 아닌 일반 함수라면 사용자가 예외에 대한 대처가 가능할 것이다.

* 어떤 Class의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수 (즉, 소멸자가 아닌 함수) 이어야 합니다.

class DBConn {
public :
    ...
    void close() {
        db.close();
        closed = true;
    }
    ~DBConn() {
        if (!closed)
            try { db.close(); }
            catch (...) { ... }
    }
private:
    DBConnection db;
    bool closed;
};

close()의 호출 책임을 소멸자에서 사용자로 떠넘기게 되었다. ('확인사살' 코드) 처음 만나 술에 떡이 되어 간밤에 무슨일이 생겼는지도 모르는 춘삼월의 순박한 미팅커플처럼 무책임한 책임 전가로 보일수도 있다. 하지만 예외를 처리할 필요가 있다면 그 예외는 소멸자가 아닌 다른 함수에서 해야 한다.



댓글 없음:

댓글 쓰기