본문 바로가기

Effective C++/2. 생성자, 소멸자 및 대입 연산자

항목 8: 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

예외가 터져 나오는 것을 내버려두는 소멸자 ==> 프로그램 강제종료 또는 미정의 동작의 원인

 

만약 예외를 던졌는데 예외처리에 실패할 있는 코드를 소멸자에 넣아야 한다면 어떻게 ??

 

데이터베이스 연결을 나타내는 클래스를 쓰고 있다고 가정해보자.

 

 

보다싶이 사용자가 DBConnection 객체에 대해 close 직접 호출해야하는 설계이다. 만약 사용자의 망각을 사전에 차단하는 좋은 방법이라면 DBConnection 대한 자원 관리 클래스를 따로 만들어서 클래스의 소멸자에서 close() 호출하게 하는 것이다. 3장에서 이런 자원 관리 클래스를 충분히 다루고 있지만, 대충 살펴보면

 

 

 

 

close 호출만 일사천리로 성공하면 아무 문제될 것이 없다. 하지만 close()에서 예외가 발생하면??? DBConn 소멸자는 예외를 전파할 것이다.

=> 소멸자 안에 별도의 예외 처리기를 만들지 않았으면 OS 기본예외처리기로 바로 알릴 것이다!!

=> 소멸자 외부로 예외가 알려진다는 것인데…

=> 소멸자가 밖으로 예외를 던지는 꼴이 된다.

 

이걸 피하는 방법은 2가지가 있다.

 

  • close()에서 예외가 발생하면 프로그램을 바로 종료시킨다.( absort 호출 )

 

 

객체 소멸을 진행하다 Exception 떴는데, 예외가 치명적이다(프로그램 실행을 계속할 없다) => 프로그램 강제 종료

 

만약 예외를 끌고 가다가 미정의 동작까지 같다고 하면 좋은 선택이다.

 

 

  • close() 호출한 곳에서 일어난 예외를 삼켜버린다.

 

 

대부분의 경우에서 예외를 그대로 삼키는 경우는(예외를 받기만 하고 프로그램 강제 진행) 좋은 발상이 아니지만(중요한 정보(예외 정보) 묻혀버림), 때에 따라서 불완전한 프로그램 종료 미정의 동작을 방지하는게 예외 정보를 알리는 보다 중요할 있다.

 

예외 삼키기가 빛을 볼려면 예외를 무시하더라도 프로그램이 신뢰성 있게 실행을 지속할 있어야 한다.

 

단점이 있다. 중요한 것은 close() 최초로 예외를 던지게 요인에 대해 프로그램이 어떤 조취를 취할 있는것인가 인데, 이런 요인에 대해 대책이 없기 때문이다. => 근본적으로 close() 예외를 안던지게 하는 대책이 없음

 

좋은 전략 고민중…

 

DBConn 인터페이스를 설계해서 발생할 수도있는 문제에 대해 사용자가 처리할 있게 하면 어떨까??

=> 소멸자의 에러에 대해 사용자가 직접 처리할 있게 (DBConnection close() 직접 제공하는 것이랑은 다름)

 

예를 들어 DBConn에서 close() 직접 제공하면? 함수의 실행 중에 발생하는 예외를 사용자가 직접 처리할 있을 것이다.

 

 

close 호출의 책임을 DBConn 소멸자에서 사용자로 넘기는, DBConn 소멸자엔 사용자가 책임을 회피해도 확인사살하는 코드를 넣는 이런 아이디어는 너무 무책임하거나 제대로 쓰기 어려운 인터페이스를 제공하는거 같기도 한데,,

 

어떤 동작(close()) 예외를 일으키면서 실패할 가능성이 있고, 예외를 처리해야 필요가 있다면, 예외는 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다 것이 포인트이다.

 

코드를 보면 사용자가 DBConn.close() 하였을 db.close()에서 예외가 발생하면 사용자가 try catch문을 통해 해결할 있고(closed true 아니면 예외 발생한 것임), 소멸자에서 예외가 발생한 경우는 확인사살로 프로그램을 강제 종료하거나, 예외를 숨겨버리는 대책을 내세울 있다.

 

예외를 일으키는 소멸자 => 시한폭탄이다. 왜냐하면 소멸자에서 예외가 발생하면 객체가 완전히 소멸하지 않고 객체 속의 중요한 정보, 메모리 자원들이 줄줄 세는 현상이 발생할 있고, 이로 인해 프로그램의 불완전 종료 또는 미정의 동작의 위험을 내포하고 있기 때문이다.

 

다시 코드를 보면 사용자가 호출 있는 DBConn.close()함수를 두긴 했지만, 부담을 사용자한테 떠넘기는 행태가 아니라, 사용자가 에러를 처리할 있게 해주는 것이라 봐야 한다. 이것 마저 없으면 사용자는 DBConnection에서 발생하는 예외 자체를 처리하지 못한다.

사용자가 이런 에러처리의 기회를 무시할 수도 있지만, 소멸자가 어떻게든 마무리 지어줄 것이고, 때는 DBConn 예외를 삼켜버리든 프로그램을 강제 종료하든 사용자 쪽에선 불평을 못한다. 기회를 주고 있기 때문이다.

 

  • 소멸자에서는 예외가 빠져나가면 안된다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 삼켜버리던지, 프로그램을 강제 종료하던지 해야함
  • 어떤 클래스의 연산이 진행되다 던진 예외에 대해 사용자가 반응해야 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(소멸자가 아닌 함수)이어야 한다. => 소멸자가 밖으로 예외를 던지면 안됨