상세 컨텐츠

본문 제목

C++ 예외 지정

C++/C++98

by deulee 2023. 8. 9. 09:08

본문

미처리 예외

throw가 예외를 던졌는데 이를 받아줄 catch가 없는 경우 미처리 예외가 된다.

 

미처리 예외는 "terminate"라는 함수가 처리하는데 이 함수는 기본적으로 abort를 호출하여 프로그램을 강제로 종료한다.

 

만약 이를 특별한 방식으로 처리하고 싶다면 예외의 핸들러를 따로 등록할 수 있다.

 

#include <exception>

terminate_handler set_terminate(terminate_handler ph) // void func(void) 타입의 함수 포인터를 전달함

예시를 한번 보도록 하자.

 

#include <iostream>
#include <exception>

void myTerm()
{
	std::cout << "미처리 예외 발생" << std::endl;
	exit(-1);
}

int main(void)
{
	std::set_terminate(myTerm);
	try {
		throw 1;
	}
	catch (const char* message) { // 처리 안함
	}
	return 0;
}

위의 예시처럼 정수형 예외를 던졌지만 이를 받아줄 catch가 존재 하지 않는다. 이때 "set_terminate(myTerm);"으로 전역적인 미처리 핸들러를 이용하여 미처리 예외를 다루는 모습을 알 수 있다.

 

하지만 예외 발생 사실만 중요하고 정보는 필요 없을 때 다음과 같은 키워드를 사용할 수 있기는 하다.

catch (...) {
	std::cout << "What?" << std::endl;
}

"catch (...)"는 처리되지 않은 모든 예외를 받게 된다. 그렇기 때문에 catch를 놓는 순서도 중요하다. 만약 이게 맨 앞에 선언되면 모든 예외를 이 구문이 가져갈테니 말이다.

 

또한, catch는 암시적인 타입 변환을 지원하지 않는다. 즉, 예외 객체는 정확한 타입만 찾는다.

 

단, 예외적으로 "void *" 타입을 받는 핸들러는 "임의의 포인터 타입 객체를 받을 수 있고" "부모 포인터 타입"을 받는 핸들러는 "자식 객체를 받을 수 있다".

 


예외 지정

함수를 작성할 때 함수의 원형 뒤쪽에 이 함수의 실행중에 발생할 수 있는 예외의 종류를 지정할 수 있다.

void func(int a, int d) throw(char *)

요런 식으로 말이다.

 

이는 인수의 수는 신경쓰지 않는다.

void func(int a, int d) throw(char *, int, double)

만약 예외를 던지지 않는 함수는 다음과 같이 "throw ()" 괄호를 비워둔다.

void func(int a, int d) throw() // 아무런 예외를 던지지 않는다.
void func(int a, int d) // 임의의 예외를 던질 수 있다.

이는 일종의 주석이라고 볼 수 있는데, 개발자는 원형 뒤쪽에 적절한 catch문을 작성할 수 있게 된다.

 

하지만 다음같은 상황은 어떤가?

void fA() throw(int, double)
{}

void fB() throw(char)
{
	fA();
}

"fA()"가 int, double 예외를 던질 수 있으므로 이 함수를 호출하는 "fB()"는 char, int, double 다 명시해야 하는 것이 원칙이다.

 

하지만, 예외 지정이 제대로 되어 있지 않기 때문에 위의 지정이 틀렸다고 해서 "fA"를 호출하는 것을 금지하는 것은 말이 안된다. 왜냐하면 자신이 호출하는 함수가 내부적으로 호출하는 함수 목록을 정확히 파악한다는 것은 현실적으로 말이 안되기 때문이다.

 

이런 상황에서 지정하지 않은 예외가 발생한다면 이때는 "unexpected"라는 함수가 호출되어 미지정 예외를 처리한다. 이 또한 디폴트로 terminate를 호출하여 프로그램을 강제로 종료한다.

 

이 또한, 다음의 핸들러로 동작을 설정할 수 있다.

unexpected_handler set_unexpected(unexpected_handler ph);

다음 예시로 글을 마무리 하겠다.

#include <iostream>
#include <exception>

void myFun()
{
	std::cout << "unexpected Error" << std::endl;
	exit(-2);
}

void calc() throw(int)
{
	throw "string";
}

int main(void)
{
	std::set_unexpected(myFun);
	tyr {
		calc();
	}
	catch (int) {
	}
	return 0;
}

위의 예제에서 "exit()" 함수를 호출했는데 이 대신 "throw 1"을 던지며 지정된 타입의 예외 핸들러로 제어를 옮길 수 있다.


출처

http://www.soen.kr/lecture/ccpp/cpplec.htm

 

C/C++ 강좌

 

www.soen.kr

 

'C++ > C++98' 카테고리의 다른 글

C++ RTTI  (0) 2023.08.09
C++ 예외의 비용  (0) 2023.08.09
C++ 표준 예외  (0) 2023.08.08
C++ try 블록 함수  (0) 2023.08.08
C++ 예외 객체  (0) 2023.08.08

관련글 더보기