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