개발지식창고/Effective C++
항목 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
모과이IT
2010. 11. 14. 23:45
- #include <iostream>
- #include <vector>
- using namespace std;
- class DBConnection
- {
- public:
- //DBConnection 객체를 반환하는 함수.
- static DBConnection create()
- {
- cout << "DBConnection::create()" << endl;
- DBConnection temp;
- return temp;
- }
- //연결을 닫는다. 연결이 실패하면 예외를 던진다.
- void close(){ cout << "DBConnection::close()" << endl; }
- };
- class DBConn
- {
- public:
- DBConn(DBConnection temp)
- {
- cout << "DBConn() 생성자" << endl;
- db = temp;
- }
- // 데이터베이스 연결이 항상 닫히도록 확실히 챙겨주는 함수
- ~DBConn()
- {
- cout << "~DBConn() 소멸자" << endl;
- db.close(); // close()함수에서 에러가 발생하면 문제가 발생한다.
- }
- private:
- DBConnection db;
- };
- int main()
- {
- // DBConnection 객체를 생성하고 이것을 DBConn 객체로 넘겨서 관리를 맡긴다.
- DBConn dbc(DBConnection::create());
- return 0;
- }
문제해결 하나! |
- DBConn::~DBConn()
- {
- cout << "~DBConn() 소멸자" << endl;
- try
- {
- db.close();
- }
- catch(...)
- {
- // close 호출이 실패했다는 로그를 작성.
- abort();
- }
- }
문제해결 둘! |
- DBConn::~DBConn()
- {
- cout << "~DBConn() 소멸자" << endl;
- try
- {
- db.close();
- }
- catch(...)
- {
- // close 호출이 실패했다믄 로그를 작성.
- }
- }
하지만 위 둘의 방법은 각자의 문제점을 가지고 있습니다. 중요한것은 close가 최초로 예외를 던지게 된 요인에 대해 프로글매이 어떤
조치를 취할 수 있는가인데, 이런 부분에 대한 대책이 없기 때문입니다. 그럼 더 나은 방법은 뭐가 있는지 살펴 보도록 하죠.
새로운 문제 해결 |
- #include <iostream>
- #include <vector>
- using namespace std;
- class DBConnection
- {
- public:
- // DBConnection 객체를 반환하는 함수.
- static DBConnection create()
- {
- cout << "DBConnection::create()" << endl;
- DBConnection temp;
- return temp;
- }
- // 연결을 닫는다. 연결이 실패하면 예외를 던진다.
- void close(){ cout << "DBConnection::close()" << endl; }
- };
- class DBConn
- {
- public:
- DBConn(DBConnection temp)
- {
- cout << "DBConn() 생성자" << endl;
- db = temp;
- }
- void close()
- {
- cout << "close() 함수 호출" << endl;
- db.close();
- closed = true;
- }
- ~DBConn()
- {
- cout << "~DBConn() 소멸자" << endl;
- if (!closed)
- {
- try
- {
- cout << "try::close() 함수 호출" << endl;
- db.close(); // 사용자가 연결을 안 닫았으면 여기서 닫는다.
- }
- catch(...)
- {
- // close 호출이 실패했다는 로그를 작성.
- }
- }
- }
- private:
- DBConnection db;
- bool closed;
- };
- int main()
- {
- DBConn dbc(DBConnection::create());
- dbc.close();
- return 0;
- }
위의 예제를 보면 사용자가 호출할 수 있는 close 함수를 둬서, 사용자에게 에러를 처리할 수 있는 기회를 주고 있습니다. 이것마저
없다면 사용자는 예외에 대처할 기회를 포착하지 못하게 될테죠. 사용자가 이 기회를 무시 했다고 해도 DBConn이 close 함수를 호출해
줄것이므로 문제는 없습니다.
* 소멸자에서는 예외가 빠져나가면 안됩니다. 만약
소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야
합니다.
* 어떤 클래스의 연산이 진행되다가 던진 예외에
대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(즉, 소멸자가 아닌 함수)이어야
합니다.