try catch 그리고 throw


if 문을 사용하여 예외문을 구성할 수 있지만 , 코드의 가독성을 위하여 C++ 은 try catch 메커니즘을 제공한다. 


try

{

// 예외처리 발생지역

}


try 블록은 예외발생에 대한 검사의 범위를 지정할때 사용한다. 즉 , try 블록 내에서 예외가 발생하면 C++ 예외처리 메커니즘에 의해 처리된다. try 블록 내에서 예외가 발생하면 catch 블록이 실행되고나서, 예외가 발생한 지점 이후를 실행하는것이 아니라, catch 블록의 이후가 실행된다.



catch( 처리할 예외객체의 자료형 )

{

//예외처리 코드의 삽입

}


catch 블록은 try 블록에서 발생한 예외의 처리를 담당하는 코드가 담기는 영역이다. catch 블록은 try 블록의 바로 뒤에 이어서 등장한다. ( 중간에 다른 문장이 올 수 없다. )


throw expn;


키워드 throw는 예외가 발생했음을 알리는 문장의 구성에 이용된다. throw에 의해 던져진 예외데이터는 예외데이터를 감싸는 try 블록에 의해서 감지가되어 이어서 등장하는 catch 블록에 의해 처리된다. 이때, throw 절에 의해 던져진 예외데이터의 자료형과 catch 블록의 매개변수 자료형은 일치해야한다.


예외의 전달


함수 내의 throw 절에의해 예외가 발생했을때 , 예외를 처리하기위한 try catch 문이 함수 내에 존재하지않아서 예외처리가 되지않았다면 , 예외처리에 대한 책임은 그 함수를 호출한 영역으로 넘어가게된다. ( 그 함수가 호출된 절을 감싸는 try 문에의해 감지된다 )

또한 , 함수 내에서 함수를 호출한 영역으로 예외데이터를 전달하면 , 해당 함수는 더이상 실행되지않고 종료된다 . 


스택 풀기


예외처리가 되지 않아서 함수를 호출한 영역으로 예외 데이터가 전달되는 현상을 가리켜 스택풀기라고한다. 예외가 처리될때까지 호출된 함수의 역순으로 예외데이터가 전달되며 함수의 종료가 이루어지므로 함수의 스택이 소멸된다고 하여 스택풀기라고 한다.

main 함수까지 예외처리가 전달되었는데 , main 함수에서도 예외를 처리하지못하면 terminate 함수 ( 프로그램을 종료시키는 함수 )가 호출되면서 프로그램이 종료된다.

throw가 던진 자료형과 catch 가 받는 자료형이 다르다면 이때도 같은 자료형을 받는 catch 가 나올때까지 예외가 전달된다.


하나의 try 블록과 다수의 catch 블록


자료형을 달리하여 try 블록 내에서 발생하는 둘 이상의 예외를 처리할 수 있다.


전달되는 예외의 명시


예외가 있는 함수를 정의할때는 해당 함수의 throw 호출문장을 감싸는 적절한 try catch 블록을 구성하기 쉽게하기위해 함수 내에서 발생한 예외의 종류를 명시해 주는것이 좋다.


int ThrowFunc(int num) throw( int ,  char )

{

...

}


try 

{

ThrowFunc(20);

}

catch(int expn){  .. }

catch(char expn){ ... }


위처럼 선언될경우 함수로부터 int 혹은 char 데이터의 예외가 전달되어야하며 , 다른 종류의 데이터가 전달될경우 terminate 함수가 호출되고 , 프로그램이 종료된다.


int SimpleFunc(void) throw ()

{

....

}


위의 함수에는 전달되는 예외의 자료형을 명시하는부분이 비어있다 . 이는 이 함수가 어떠한 예외도 전달하지않음을 의미하며 , 예외가 전달될경우 프로그램이 종료된다.


예외 클래스의 설계


예외 발생을 알리는데 사용되는 객체를 가리켜 예외객체라고 하며 , 이 객체의 생성을 위해 정의된 클래스를 예외클래스라고 한다.


class DepositException

{

private :

int reqDep;

public :

DepositException(int money) : reqDep(money)

{ }

void ShowExceptionReason()

{

cout<<"예외메세지 "<<reqDep<<"은 입금 불가" <<endl;

}

};

class Account

{

private :

char accNum[50];

int balance;

public :

Account ( char * acc, int money) : balance(money)

{

strcpy(accNum,acc);

}

void Deposit(int money) throw (DepositException)

{

if (money<0)

{

DepositException expn(money);

throw expn;

}

balance += money;

}

};

int main (void)

{

Account myAcc("12345-12345",4000);


try

{

myAcc.Deposit(2000);

myAcc.Deposit(-100);

}

catch ( DepositException expn)

{

expn.ShowExceptionReason();

}

return 0;

}


상속관계를 이용한 예외클래스의 단순화


class AccountException 

{

public : 

virtual void ShowExceptionReason()  = 0; // 순수 가상함수

}

class DepositException : public AccountException

{

private :

.....

public :

....

void ShowExceptionReason() // 순수 가상함수 오버로딩

{

cout<< "예외메시지 : "<<reqDep<<"는 입금할 수 없습니다 " <<endl;

}

};


예외의 전달방식에 따른 주의사항


AAA객체를 BBB객체가 상속하고, BBB객체를 CCC 객체가 상속하는경우 , AAA BBB CCC 객체들이 전부 예외를 던지면 catch 문을 구성할때는 CCC BBB AAA 객체의 예외를 전달받는 순서로 선언해야한다. 이유는 AAA 객체를 먼저 받게되면 , CCC 객체는 AAA 객체를 상속하므로 AAA 객체의 예외를 받는 catch 문에서 처리되기 때문이다.


예외처리와 관련된 또다른 특성들


new 연산자에 의해서 발생하는 예외


new 연산에 의한 메모리 공간의 할당이 실패하면 bad_alloc 이라는 예외가 발생한다. bad_alloc 은 헤더파일 new에 선언된 예외클래스로서 메모리공간 할당이 실패했음을 알리는 용도로 사용된다.


모든 예외를 처리하는 catch 블록


catch ( ... ) // ...은 전달되는 모든 예외를 전부 받아주겠다는 선언이다.

{

...

}

보통 마지막 catch 블록에 덧붙여지는 경우가 많다


예외 던지기


catch 블록에 전달된 예외는 다시 던져질 수 있다 . 이로 인해서 하나의 예외가 둘 이상의 catch 블록에 의해서 처리되게 할 수 있다.


void Divide(int num1, num2)

{

try

{

if(num2==0)

throw 0;

cout <<" 몫: " <<num1/num2<<endl;

cout<< "나머지 : "<<num1%num2<<endl;

}

catch (int expn)

{

cout << "firts catch "<<endl;

throw; // 예외 다시던지기

}

}

int main (void)

{

try

{

Divide(9,2);

Divide(4,0);

}

catch(int expn)

{

cout<<"second catch"<<endl;

}

return 0;

}



+ Recent posts