class Widget {}
vector<Widget> v;
위의 클래스가 선언되어 있고, vector<Widget> v의 사이즈가 10이라고 가정합니다.
v가 사이즈가 10인데, 첫번째 것을 소멸시키는 도중에 예외가 발생되었다고 가정하면, 나머지 아홉 개는 소멸자가 호출되지 않아 메모리 누수가 생깁니다.
위의 문제를 방지하기 위해서는 소멸자에게 예외가 나오는 것을 방지해야 합니다.
예를 들어봅시다.
ex ) DB를 접속하는 클래스
class DBConnection {
public:
/**
* 생성자 소멸자가 아닌 따로 함수를 만들어서 생성 소멸을 할 수 있게 작업
*/
static DBConnection create();
void close();
};
ex) DBConnetion에 대한 자원 관리 클래스
class DBConn {
public :
~DBConn()
{
db.close();
}
객체 소멸이 진행되다가 에러가 발생한 후 더이상 프로그램을 실행할 수 없는 상황일 경우에 사용한다.
둘째. close를 홀출한 곳에서 일어난 예외를 삼켜버립니다.
예외 삼키기를 선택한 것이 제대로 빛을 보려면, 발생한 예외를 그냥 무시한 뒤라도 프로그램이 신뢰성 있게 실행을 지속할 수 있어야 합니다.
이 두가지 방법 또한 문제점들을 가지고 있습니다.
이유는?
이 두 방법은 예외가 발생한 후의 처리를 하는 것인데, 실제로 close()가 예외를 던지게 된 요인에 대
한 조치를 취하는 대책이 전무한 상태입니다.
더 나은 방법은 아래와 같습니다.
DBConn 클래스에서 close()를 직접 제공하는 방법이 있습니다.
이렇게 만들면, close() 함수가 실행 중에 발생하는 예외를 사용자가 처리할 수 있습니다.
class DBConn {
public:
...
void close() // 사용자에게 호출을 위해 정의한 close() 함수
{
db.close();
closed = true;
}
~DBConn()
{
if( !closed )
{
try{
db.close();
}
catch( ... )
{
// close 호출 실패 로그 출력
// ...
}
}
}
private:
DBConnection db;
bool closed;
};
close() 호출 책임을 소멸자에서 사용자에게로 떠넘긴다.
예외를 처리할 수 있는 기회를 사용자에게 주는 것입니다. 이것 마저 없다면 사용자는 예외에 대처할 기회를 못잡게 됩니다. ( 2차 검증으로 DBConn 소멸자에서 마무리 )
즉!
예외가 생길 수 있는 코드는 소멸자보다는 다른 함수에서 비롯되어야 한다.
이것 만은 잊지 말자!
◆ 소멸자에서는 예외가 빠져나가면 안됩니다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야합니다.
◆ 어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(즉, 소멸자가 아닌 함수)이어야 합니다.
'0x0001 > Effective C++' 카테고리의 다른 글
[Effective C++] 항목 10 : 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2019.02.11 |
---|---|
[Effective C++] 항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2019.02.10 |
[Effective C++] 항목 7 : 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2019.02.10 |
[Effective C++] 항목 6 : 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자 (0) | 2019.02.10 |
[Effective C++] 항목 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2019.02.09 |