예외가 소멸자를 떠나지 못하도록 붙들어 놓자!
그냥 소멸자안에서 예외처리를 하면 안된다는 말인거 같다.
예제를 보며 문제점과 해결방안을 짚어보겠다.
class NPC{
public:
...
~NPC() { ... };
--> 예외를 던지는 소멸자라고 가정
};
void doSomething(){
vector<NPC> n;
...
}
vector 타입의 객체 n은 자신이 소멸될 때
자신이 가지고 있는 NPC객체를 소멸시킬 책임이 있다.
하지만 NPC를 소멸시키는 도중 예외가 발생되었다고 하자.
이 경우 프로그램은 미정의 동작을 보인다.
원인은 바로 예외를 내보내는 소멸자 때문!!
(NPC의 소멸자에서 예외를 내보내고 있다)
![]()
무슨 말인지 모르겠다. 다음 예를 보도록 하자.
class DBConnection {
public:
...
static DBConnection create();
void close();
--> 연결을 닫는 함수. 연결이 실패하면 예외를 던진다고 가정.
--> 즉, 이번 항목 핵심인 예외를 던지는 함수
};
class DBConn{
private:
DBConnection db;
public:
...
~DBConn(){
db.close();
--> 소멸자에서 예외를 던지는 함수를 호출하고 있다.
}
};
위의 예제는 데이터베이스의 연결을 관리하는
클래스 DBConnection과 그의 자원은 관리하는
클래스 DBConn이다.
여기서 주목할 점은 DBConnection의 멤버 함수
close()는 실패할 경우 예외를 던진다는 점이다.
{
DBConn dbc(DBConnection::create());
...
...
}
--> DBConnection 객체를 생성하고 DBConn으로
넘겨주어 관리를 맡기고 있다.
블록이 끝나면 DBConn 객체가 사라지고
사라지면서 자연스레 close()함수를 불러 연결을 닫게 된다.
![]()
하지만 close()는 실패할 경우 예외를 던진다!
때문에 close()를 호출하는 DBConn의 소멸자는
이 예외를 전파할 것이다. --> 문제 발생
![]()
해결책
1. 예외가 발생하면 프로그램을 끝내버린다.
~DBConn::~DBConn(){
try{ db.close() }
catch( ... ) {
...
abort();
}
}
--> close()에서 예외를 던졌다.
걍 프로그램을 끝내버린다.
미정의 동작으로 가기보다는 그냥 끝내버리겠다는 방법
![]()
2. 예외가 발생하면 삼켜버리겠다.
~DBConn::~DBConn(){
try{ db.close() }
catch( ... ) {
...
}
}
--> 그냥 무시하고 진행하겠다는 것.
무엇이 잘못되었는지 알 수 없다.
예외를 무시하더라도 그 다음 동작이
제대로 이루어 진다는 보장이 있을 때 사용.
![]()
--> 두가지 모두 좋은 해결책은 아니다.
사용자가 직접 해결할 수 잇도록 기회를 주도록 하자!
class DBConn{
private:
DBConnection db;
bool closed;
public:
...
void close(){
db.close();
closed = true;
}
~DBConn(){
if(!close)
try{
db.close();
}
catch( ... ){
...
}
}
--> 사용자가 연결을 직접 닫을 수 있게 close()함수를 제공
--> 즉, 사용자가 함수를 직접 불러 연결을 닫음
--> 따라서 발생하는 예외 또한 사용자가 처리해야함.
![]()
--> DBConn 객체가 소멸될 때, 사용자가 이미 close()함수를 불러
연결을 닫은 후라면, 소멸자에서 예외가 발생할 경우가 없어지게 된다.
--> 연결이 열려있다면 소멸자 내의 db.close() 함수를 호출해서 닫아 준다.
--> 닫다가 실패하면 위의 두가지 방법 중 하나
예외를 먹던지, 종료를 하던지 한다.
![]()
같은 말을 계속 반복하는거 같지만
쉽게 말해서 DBConn 클래스에 close()함수로
DBConnection의 연결을 직접 닫겠다는 것이다.
DBConn의 close()를 호출하기 되면 closed = true가 될 것이고
DBConn의 소멸자에서 예외를 던지지 않게되는 것이다.
--> 쉽게 말해 사용자가 직접 닫아라.
여기서 핵심!!
--> 예외는 소멸자가 아닌 다른 함수에서 비롯되어야 한다.
예외를 던지는 소멸자는 시한폭탄과도 같다.
![]()
POINT!!
1. 소멸자에서는 예외가 던져지면 안된다.
소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면
삼키던지 종료하던지 해라.
2. 어떤 클래스의 연산이 진행되다가 던진 예외에 대해
사용자가 직접 처리해야 할 경우, 해당 연산을 제공하는 함수는
반드시 보통의 함수여야 한다. (소멸자 노노)
'C++ 심화' 카테고리의 다른 글
| [C++] 대입 연산자와 *this 참조자 (0) | 2016.12.19 |
|---|---|
| [C++] 객체 생성, 소멸 시의 가상 함수 호출 (0) | 2016.12.19 |
| [C++] 가상(virtual) 소멸자 (0) | 2016.12.18 |
| [C++] 컴파일러가 만들어낸 함수가 필요 없을 때 (0) | 2016.12.18 |
| [C++] 생성자, 소멸자, 대입 연산자에 주의를 기울이자. (0) | 2016.12.15 |