일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 스마트 포인터
- 영화
- 티스토리챌린지
- Smart Pointer
- lua
- 오블완
- resource management class
- 루아
- reference
- 예외
- 다형성
- virtual function
- 참조자
- operator new
- 상속
- 암시적 변환
- exception
- effective stl
- c++
- Effective c++
- implicit conversion
- 반복자
- 언리얼
- 메타테이블
- 비교 함수 객체
- more effective c++
- 게임
- Vector
- UE4
- 영화 리뷰
Archives
- Today
- Total
스토리텔링 개발자
[More Effective C++] 14. 예외 지정(예외 명세) 본문
728x90
항목 14. 예외 지정(exception specification) 기능은 냉철하게 사용하자
예외 지정(exception specification)
- 함수를 선언할 때 함수가 발생시킬 예외를 미리 지정하는 기능
- 장점
- 어떤 함수가 어떤 예외를 발생시키는지가 드러나기에 코드 가독성이 좋아진다.
- 예외 지정에 일관성이 없으면 컴파일러가 컴파일 중 발견해준다.
- 함수가 예외 지정 리스트에 없는 예외를 발생시킬 경우
- 런타임 에러가 발생하면서 unexpected라는 특수 함수가 자동으로 호출된다.
- 단점
- unexpected 함수의 기본 동작은 std::terminate를 호출하는 것이다.
- terminate는 기본적으로 abort를 호출하는데, 이는 프로그램을 바로 멈춰버린다.
- 활성 스택 프레임에 만들어진 지역 변수는 소멸되지 않는다.
- 컴파일러가 해주는 예외 지정 일관성 점검이 극히 부분적이다.
- 예외 지정 함수 내에서 예외 지정을 어기는 함수를 호출하는 부분까지는 점검해 주지 않는다.
- C++ 표준안에 의하면, 경고는 내더라도 거부는 하면 안되도록 되어 있다.
-
extern void f1(); // 예외 지정이 없는 함수 void f2() throw(int); // int 타입 예외만을 발생시키는 함수 void f2() throw(int) { ... f1(); // f1은 int 이외 타입의 예외를 발생시킬 수 있다. // 하지만 컴파일러는 별다른 말을 하지 않는다. // 즉, 문법은 맞다. ... }
- 예외 지정 함수 내에서 예외 지정을 어기는 함수를 호출하는 부분까지는 점검해 주지 않는다.
- 템플릿에는 사용이 불가능한 것과 마찬가지이다. (아래에서 자세히 설명)
- 예외 처리 코드가 준비된 상황에서조차 unexpected가 호출될 수 있다.
-
class Session { public: ~Session(); ... private static void logDestruction(Session* objAddr) throw(); // 예외가 발생하지 않도록 예외지정 }; Session::~Session() { try { logDestruction(this); // 허나.. logDestruction 내부의 어떤 함수에서 예외가 발생한다면? // unexpected가 호출되면서, // 외부에서 준비된 try / catch 문은 실행조차 되지 않고 프로그램이 멈춘다!! } catch(...) { } // 모든 예외를 붙잡아 처리한다. }
-
- 모던 C++에서는 더이상 지원하지 않는 기능이다. (C++14 이후부터)
- 일반적인 예외 지정은 지원하지 않는다.
- 예외 발생 금지(throw())의 경우 noexcept 키워드 사용이 권장된다.
- unexpected 함수의 기본 동작은 std::terminate를 호출하는 것이다.
예외지정 불일치 피하기
1. 템플릿에 예외 지정을 두지 않는다.
- 템플릿의 타입 매개변수에서 발생된 예외에 대해 알 방법이 없다.
- 템플릿이 받아들이는 타입 매개변수는 무궁무진하기 때문이다.
// 예외 지정에 대해 신경쓰지 않은 템플릿
// 예외가 발생하지 않도록 지정되어 있다.
// 하지만...
// 어떤 타입이 &operator를 오버로딩 했다면?
// 거기서 예외가 발생할 수도 있고, 그렇다면 unexcepted 행이다.
template<typename T>
bool operator==(const T& lhs, const T& rhs) throw()
{
return &lhs == &rhs;
}
2. 예외 지정이 안된 함수를 호출할 가능성이 있는 함수에는 예외지정을 두지 않는다.
- 하지만.. 이게 가능한 일일까?
// 콜백을 위한 함수 포인터
typedef void (*CallBackPtr) (int eventXLocation,
int eventYLocation,
void* dataToPassBack);
class CallBack
{
public:
CallBack(CallBackPtr fPtr, void* dataToPassBack)
: func(fPtr), data(dataToPassBack) {}
void makeCallBack(int eventXLocation,
int eventYLocation) const throw();
private:
CallBackPtr func; // 콜백이 일어날 때 호출되는 함수
void* data; // 콜백 함수로 넘겨지는 데이터
};
// 콜백 함수 호출
// 허나, func는 예외 지정을 어길 위험을 안고 있다.
// func가 예외를 던질지 말지, 어떤 예외를 던질지 전혀 알 방법이 없다.
void CallBack::makeCallBack(int eventXLocation,
int eventYLocation) const throw()
{
func(eventXLocation, eventYLocation, data);
}
typedef void (*CallBackPtr) (int eventXLocation,
int eventYLocation,
void* dataToPassBack) throw();
// CallBackPtr에 예외지정을 함으로써 해결해보자.
// 참고) 하지만... C++ 표준으로는 예외 지정은 typedef에 넣으면 안된다고 해서 여전히 문제 있음..
void* callBackData;
void callBackFcn1(int eventXLocation, int eventYLocation, void* dataToPassBack);
CallBack c1(callBackFcn1, callBackData); // 에러! 예외 지정이 없다.
void callBackFcn2(int eventXLocation, int eventYLocation, void* dataToPassBack) throw();
CallBack c2(callBackFun2, callBackData); // 성공
3. "시스템"이 일으킬 가능성이 있는 예외(C++ 표준 예외)를 처리하도록 한다.
- 가장 흔한 C++ 표준 예외는 bad_alloc이다.(항목 8 참조)
- 예기치 않은 예외의 발생을 막는 것보다 그 예외와 만나기가 더 쉽다.
- 예외 지정을 사용하고 있는데, 사용자가 사용하게 된 라이브러리 함수가 예외 지정을 사용하지 않는다면?
- C++에는 예기치 않은 예외를 다른 타입 예외로 대체할 방법이 있다.
- set_unexpected 를 사용하는 것이다.
- 사실 여기에 적용되어 있는 디폴트 함수가 바로 unexpected 함수이다.
class UnexpectedException {}; // 모든 예기지 않은 예외는 이 타입 객체로 대체한다.
void convertUnexcepted() // 예기지 않은 예외 발생 시 호출할 함수
{
throw UnexpectedException();
}
set_unexpected(convertUnexpedted); // 예기치 않은 예외 발생 시 예외 대체!
// 이제 예외 지정 리스트에 UnexpectedException을 넣으면 된다.
- 예기치 않은 예외를 잘 알려진 타입으로 바꾸는 방법도 가능하다.
- 이를 통해 C++ 표준 예외로 변경이 가능하다!
void convertUnexpected()
{
throw; // 그냥 다시 던진다.
// 이 경우 그냥 bad_exception 객체로 바뀐다.
}
set_unexpected(convertUnexpected);
// unexpected 대신 그냥 다시 던지도록 셋팅한다.
// 이제 예외 지정 리스트에 bad_exception을 넣으면 된다.
참조
728x90
'개발 > More Effective C++' 카테고리의 다른 글
[More Effective C++] 16. 파레토 법칙 (0) | 2024.08.21 |
---|---|
[More Effective C++] 15. 예외 처리 비용 (0) | 2024.08.20 |
[More Effective C++] 13. catch 매개변수는 참조자 (0) | 2024.08.16 |
[More Effective C++] 12. 예외 전달 vs 함수 매개변수 전달 (0) | 2024.08.14 |
[More Effective C++] 11. 소멸자 예외 처리 (0) | 2024.08.13 |
Comments