센로그
[EC++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 본문
소멸자에서 예외를 던진다면?
- 소멸자에서 예외를 던진다면 사용자는 예외에 대처할 기회가 없어진다.
- 소멸자는 자원 해제에 많이 사용하므로, 예외를 던지면 자원 해제가 완료되지 않았을 위험이 크다.
- C++에서는 예외 중첩(이미 처리 중인 예외 + 새로운 예외)이 발생하면 프로그램이 강제종료된다.
소멸자에서 예외를 던질때의 예시
- 크기가 10인 클래스 A타입의 벡터의 소멸자를 호출하는 경우
- 만약 첫번째 원소의 소멸자에서 예외가 발생한다면, 예외를 전파할 것이다.
- 두번째 원소의 소멸자에서도 예외가 발생한다면, 이미 첫번째 예외를 처리중이므로 예외가 중첩된다.
- C++ 표준에서는 예외가 중첩되면 std::terminate()를 호출하여 프로그램이 강제로 종료된다.
- DBConnection 클래스의 소멸자에서 db.close()를 호출하는 경우
- 소멸자에서 예외가 발생한다면 예외가 전파되어 소멸자에서 예외가 나가도록 내버려둔다.
해결 방안
- close 예외가 발생하면 프로그램을 즉시 끝낸다.
- try catch문을 사용하여, catch문에 넘어오면 실패 로그를 띄우고 abort를 호출한다.
- 에러 발생 후에 프로그램 지속이 어려운 경우에 이 방식을 선택한다.
- close를 호출한 곳에서 예외를 삼켜버린다.
- try catch 문에서 예외 로그만 띄우고 넘어가는 것을 의미한다.
- 대부분의 경우에서 좋은 발상은 아니나, 경우에 따라서는 불완전한 프로그램 종료 혹은 미정의 동작으로 인해 입는 위험을 감수하는 것보다 그냥 예외를 먹어버리는 것이 나을 수도 있다.
- 단, 예외를 그냥 무시한 뒤라도 프로그램이 신뢰성 있게 실행을 지속할 수 있어야 한다.
- close 호출 책임을 소멸자에서 사용자에게로 넘긴다.
- DBConnection이 닫혔는지 여부를 유지하다가, 닫히지 않았으면 소멸자에서 닫을 수 있도록 하는 것이다.
- 물론 소멸자에서까지 실패하면 위의 두 방법으로 돌아온다.
어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 (소멸자가 아닌) 보통의 함수여야 한다.
- 사용자에게 에러를 처리할 수 있는 기회를 주는 것이다.
추가
+) 예외 처리 유형
- throw: 예외를 발생시켜 호출자에게 알림.
- throw 키워드 뒤에 전달된 값을 예외 객체로 생성하고 위로 던진다. 이는 catch 블록에서 사용할 수 있다.
- throw; 를 사용하면 동일한 예외를 다시 위로 던질 수 있다.
- try-catch: 예외가 발생할 가능성이 있는 코드와 그 예외를 처리하는 코드.
- try: 예외가 발생할 가능성이 있는 코드를 감싼다.
- catch: throw된 예외를 받아서 처리한다.
- nonexcept: 해당 함수가 절대 예외를 던지지 않음을 명시하는 키워드
+) 예외가 발생하면 일어나는 일
- 현재 실행 흐름 중단
- 현재 함수의 실행이 즉시 중단되며, 이후 코드는 실행되지 않는다.
- 예외 객체 전달
- throw 키워드 뒤에 전달된 값을 예외 객체로 생성하고 위로 던진다. 이는 catch 블록에서 사용할 수 있다.
- throw; 를 사용하면 동일한 예외를 다시 위로 던질 수 있다.
- 스택 해제
- 예외가 발생한 함수에서 호출 스택을 거슬러 올라가며, 해당 함수를 호출한 상위 함수로 예외를 전달한다.
- 이 과정에서, 예외를 처리하지 않는 함수는 스택에서 제거되며, 지역 변수의 소멸자가 호출된다.
- 예외가 발생한 함수나 호출 스택 상위에 catch 블록이 존재한다면, 예외를 잡아서 처리한다.
- 예외가 발생한 함수에서 호출 스택을 거슬러 올라가며, 해당 함수를 호출한 상위 함수로 예외를 전달한다.
- 예외를 처리하지 못한다면
- 예외를 처리할 catch 블록을 찾지 못하면, std::termincate()를 호출하여 프로그램을 강제종료한다.
'Effective > Effective C++' 카테고리의 다른 글
[EC++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2024.11.17 |
---|---|
[EC++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2024.11.17 |
[EC++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2024.11.16 |
[EC++] 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자 (3) | 2024.11.13 |
[EC++] 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (2) | 2024.11.13 |
Comments