코드에서 예외 처리를 하는 방법은 많이 존재한다.
주로 조건문을 사용해서 예외 처리를 하게 되지만,
C++에서 예외 처리 메커니즘인 try, catch, throw문에 대해서 살펴 보도록 하자.
순서는 try안에서 throw를 해주면
catch에서 예외를 처리 한다고 보면 된다.
예제는 다음과 같다.
#include <iostream>
int divide( int a, int b );
void main()
{
int a, b;
std::cout << "두 개의 숫자 입력 : " << std::endl;
std::cin >> a >> b;
try
{
// 예외가 발생하면,
if( b == 0 )
throw b; // throw로 넘겨주면,
std::cout << " a/b의 몫 : " << divide(a,b) << std::endl;
}
catch(int exception) // 여기서 예외를 실행한다.
{
std::cout << exception << " 입력 . " << std::endl;
std::cout << "입력 오류 ! 다시 실행 하세요. " << std::endl;
}
}
int divide( int a, int b )
{
if( b == 0 )
throw b;
return a/b;
}
구문 자체가 어색하게 느껴질 수도 있지만, 사용법이 어려운것은 아니다.
예외가 발생하면 throw b를 통해 catch에서 b를 인자로 받게 되고,
그 밑에 예외 처리문을 실행하게 되는 것이다.
단지 특이한 점은 예외 처리가 일어 나면 throw를 기준으로 그 밑 라인들은 실행을 하지 않는 다는 것이다.
이런 상황을 Stack Unwindung(스택 풀기)라고 한다.
좀더 상황을 만들면 다음과 같다.
#include <iostream>
int divide( int a, int b)
{
if( b == 0 )
throw b; // 호출되는 함수 안에서 throw가 존재한다.
// 문제가 생기면 스택을 빠져 나와 main으로 이동
// 그 후에 try문을 찾는다.
// 함수에서 또 함수를 부르고 또 다시 함수를 불러도
// throw문이 발동하면 하나씩 빠져 나와 최종적으로
// try문을 찾아 catch를 수행한다.
return a/b;
}
void main()
{
int a, b;
std::cin >> a >> b;
try
{
// try안에 throw는 없다.
std::cout << divide( a, b ) << std::endl;
}
catch( int excption )
{
// 예외가 생기면 실행
std::cout << excption << "입력" << std::endl;
}
}
이 처럼 예외가 전달되는 과정이 함수의 스택이 풀리는 순서와 일치 한다.
하지만 만약 try 나 catch가 없다면 어떻게 될까?
당연히 오류 메시지를 출력하고 프로그램을 강제 종료 하게 된다.
어떤식으로 메시지가 뜨는지 확인 하고 싶다면,
stdlib.h를 추가해서,
abort();
를 실행해 보자.
이런 식의 메시지를 확인 할 수 있을 것이다.
또 한가지 주의점은 catch에서 받는 매개변수의 타입이다.
catch( int excption ) 다음 처럼 int 형의 예외를 처리하는 catch문에을,
catch( char excption )로 변경한다면,
예외가 발생했을 때 그것을 처리하지 못하게 된다.
즉 abort가 또 실행된다.
그리고 가독성을 위해서 함수 옆에 throw가 발생 할 수 있는 예외에 대해서 설정할 수가 있는데,
int f( int a ) throw( int ) // int에 대한 throw문이 존재합니다.
int f( int a ) throw( int, double, char* ) // int나 double, char*의 throw문이 존재합니다.
int f( int a ) throw( ) // 어떤 예외도 전달하지 않습니다.
처럼 함수 내부를 다 찾아서 throw를 찾지 않아도,
함수 처음 부분만 보고도 어떤 예외가 돌아올 수 있는지 파악하여,
미리 catch문을 작성할 수 있게 도와준다고 한다.
throw에 대해서 클래스를 사용할 수도 있는데,
다음 예제를 보자.
#include <iostream>
#define PASSWORD 1234
class PasswordExpt
{
int password;
public:
PasswordExpt( int _pass ) : password(_pass){}
void what()
{
std::cout << "비밀번호가 틀립니다." << std::endl;
std::cout << password << "라고 입력" << std::endl;
}
};
void main()
{
int money = 10000;
int balance;
int password;
try
{
std::cin >> password;
// 비밀 번호가 틀리거나
if( PASSWORD != password )
throw PasswordExpt(password);
// 출금액이 부족하면,
std::cin >> balance;
if( balance > money )
throw money;
// 모든 예외에서 넘어 가면 출금
money -= balance;
}
// int 를 받는 catch
catch( int _money )
{
std::cout << "잔금이 부족합니다. 현재 잔금은" << _money << std::endl;
}
// PasswordExpt를 받는 catch
catch( PasswordExpt& expt )
{
expt.what();
}
}
따로 출력 결과를 표현하지는 않겠다. 여러 상황이 나올 수 있으니까,
한가지 확실한건 클래스를 사용해서 예외처리시에 인자로 넘겨 주었다는 것이다.
주의 할 점은 catch문은 받아 오는 인자에 따라 오버로딩 처럼 사용할 수 있지만,
실제로는 if처럼 순차적으로 검사한다는 것을 알아야 한다.
만약 AAA<-BBB<-CCC 라는 식으로 상속 구조가 되어 있다면
catch( AAA& a ) 안에서 모두다 허용할 수 있따는 것이다.
만약 이와 같은 구조를 피하고 싶다면.
catch( AAA& a );
catch( BBB& a );
catch( CCC& a );가 아닌
catch( CCC& a );
catch( BBB& a );
catch( AAA& a );로 해야 할 것이다.
'프로그래밍 > C++' 카테고리의 다른 글
문자열 관련 함수 정리(멀티바이트->유니코드->TCHAR) (0) | 2014.01.14 |
---|---|
템플릿(template) (0) | 2010.04.04 |
임시 객체 생성 (0) | 2010.04.04 |
연산자 오버로딩(operator overlonding)(2) (0) | 2010.04.04 |
연산자 오버로딩(operator overloading)(1) (0) | 2010.04.04 |