Study/Effective C++

[effective C++] Chapter 02 : 08. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

코딩뚜벅이 2023. 7. 30. 16:33

08. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

  • 예외와 소멸자
  • 이것만은 잊지 말자 !

예외와 소멸자

 

 

 위 코드는 vertor 타입의 객체 v를 생성한다. 객체 v 혹은 v2, v2 ... 등 vertor 타입의 객체 소멸에 대한 책임은 벡터에게 있다. 하지만 만약 vector 타입 객체, 여기서는 Widget의 소멸자가 호출되는 과정에서 예외가 발생된다면 어떻게 될까 ? 프로그램은 미정의 동작을 발생하고 그 원인은 바로 예외를 방치하는 소멸자에게 원인이 있다. C++은 예외를 내보내는 소멸자를 좋아하지 않는다. 더 구체적인 예시를 위해 아래의 코드를 보자.

 

 

 위 코드는 DBConnection 객체에 대해 close를 사용자가 직접 호출해야하는 설계이다. 사용자의 망각을 차단하기 위해 해당 객체에 자원 관리 클래스를 만들어서 관리하는 방법이 있는데 지금은 자원 관리 클래스의 소멸자가 어떤 형태이지만 알아보자. 

 

 

 DBConn  클래스는 DBConnection 객체를 관리하는 클래스로 DBConn의 소멸자는 DBConnection형 객체 db의 연결이 항상 닫히는 것을 보장하는 기능을 하고 있다. 따라서 위와 같은 자원 관리 클래스가 따로 있다면 처음의 코드는 아래와 같이 축약되어 사용할 수 있다.

 

 

close 호출만 문제 없이 된다면 처음에 비해 매우 효율적이 코드라 할 수 있다. 하지만 만약 여기서 예외가 발생하면 어떻게 될까 ? DBConn의 소멸자는 예외가 나가도록 내버려둘 것이고 이것이 문제이다. 이를 방지하기 위해서는 2가지 방법을 사용할 수 있다.

 

 

 첫번째, close에서 예외가 발생하면 프로그램을 바로 끝내는 방법이다. try-catch문을 이용해 close 발생 과정에서 예외가 발생한다면 abort 메서드를 통해 프로그램을 종료하는 것이다.

 

 

 두번째, close 호출이 일어난 예외를 삼켜 버리는 것이다. 삼켜버린다는 것은 에러가 발생한 원인에 대한 정보가 묻히기 때문에 좋은 발상은 아니나, 미정의 동작 혹은 프로그램 종료 등의 위험을 감수하는 것보다 예외를 먹어버리는게 나을 때도 있을 것이다. 허나 이러한 방식은 발생한 예외를 무시한 뒤라도 프로그램이 신뢰성 있게 수행될 수 있어야 한다.

 

 설명한 두 가지 방법 모두 바람직한 방법은 아니기에 조금 더 현명한 방법을 택한다면 사용자가 문제에 대처할 수 있는 방향으로 코드를 작성하는 것이다. 아래는 이를 고려해서 코드를 수정한 것이다. 하지만 이 방법 역시 소멸자가 close를 호출하는 과정에서 실패한다면 앞선 두 가지 방법 중 하나를 사용 할 수 밖에 없다.

 

 

 결국 문제에 대한 완벽한 해결을 한 것은 아니지만 중요한 것은 예외는 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다는 점이다. 예외를 일으키는 주체가 소멸자라면 이는 치명적인 결함이며 미정의 동작의 원인이 되기 때문에 예외를 일으키는 것은 소멸자가 아니도록, 예외를 던지는 함수는 보통의 함수이도록 코드를 작성해주는 것을 잊지말자.

 

 


 

이것은 잊지 말자 !

1. 소멸자에서는 예외가 빠져나가면 안된다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜버리든지 프로그램을 끝내든지 해야한다.

 

2. 어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수이어야 한다.