|
예외상황과 예외처리의 이해
예외는 프로그램의 실행 도중에 발생하는 문제상황을 의미합니다. 따라서 컴파일 시 발생하는 문법적인 에러는 예외의 범주에 포함되지 않습니다.
#include<iostream> using namespace std; int main(void) { int num1, num2; cout << "two number input: "; cin >> num1 >> num2; cout << "divide: " << num1 / num2 << endl; cout << "remaning: " << num1%num2 << endl; return 0; } |
위 예제는 두 개의 정수를 입력 받아서 정수형 나눗셈의 결과를 출력하는 예제입니다. 따라서 예외가 발생하지 않으면 다음의 실행결과를 보입니다. 그런데 제수로 사용되는 num2에 0이 입력되면 다음의 실행결과를 보입니다. 0으로 나누는 연산은 불가능하므로, 프로그램이 강제로 종료됩니다.
if 문을 이용한 예외의 처리
#include<iostream> using namespace std; int main(void) { int num1, num2; cout << "two number input: "; cin >> num1 >> num2; if (num2 == 0) { cout << "error" << endl; cout << "re program" << endl; } else { cout << "divide: " << num1 / num2 << endl; cout << "remaning: " << num1%num2 << endl; } return 0; } |
예외가 발견되는 위치는 예외가 발생하는 위치와 다를 수 있습니다. 위 예제의 경우 if문 안에서 0이 입력될 수 없음을 알리고 프로그램의 재실행을 요구하는 방식으로 예외를 처리하고 있는데, 우리에게 익숙한 이러한 예외처리방식은 다음의 단점을 지닙니다. 예외처리르 위한 코드와 프로그램의 흐름을 구성하는 코드를 쉽게 구분하지 못합니다.
C++ 의 예외처리 메커니즘
예외의 처리를 프로그램의 일반적인 흐름에서 독립시키는 것이 가능합니다.
try 예외를 발견합니다. catch 예외를 잡습니다. throw 예외를 던집니다. try 블록은 예외발생에 대한 검사의 범위를 지정할 때 사용됩니다. 즉, try 블록 내에서 예외가 발생하면, 이는 C++의 예외처리 메커니즘에 의해서 처리가 됩니다.
Catch 블록은 try 블록에서 발생한 예외를 처리하는 코드가 담기는 영역으로써, 그 형태가 마치 반환형 없는 함수와 유사합니다. try 블록 내에서 발생하는 예외는 이어서 등장하는 catch 블록에 의해서 처리됩니다. throw expn; 이의 문장엣 expn은 변수, 상수 그리고 객체 등 표현 가능한 모든 데이터가 될 수 있으나, 예외상황에 대한 정보를 담은, 의미 있는 데이터이어야 합니다. 즉, 예외가 발생하면 throw절이 실행되면, 프로그램의 흐름이 중지되고, catch 브록에 의해서 예외의 처리과정을 거치게 됩니다. 이것이 바로 C++의 예외처리 메커니즘 입니다.
예외 처리 메커니즘의 적용
#include <iostream> using namespace std; int main(void) { int num1, num2; cout << "two number input: "; cin >> num1 >> num2; try { if (num2 == 0) throw num2; cout << "divide: " << num1 / num2 << endl; cout << "remaning: " << num1%num2 << endl; } catch (int expn) { cout << "dem is " << expn << "no " << endl; cout << "re program" << endl; } cout << "end of main" << endl; return 0; } |
try 블록의 첫행을 실행하게 됩니다. 블록을 만나면, 그 안에 삽입된 코드가 실행됩니다. num2에 0이 입력되었다고 가정해봅니다. 그러면 if문은 참이 되어, 13행의 throw절이 실행됩니다. 그리고 이로 인해서 예외처리 매커니즘이 동작합니다. 예외가 발생하지 않아서 14행 15행이 실행되고 나면, 이어서 등장하는 catch 블록은 건너뛰고 22행을 실행하게 됩니다. 이로 인해서 매개변수 expn 은 num2에 저장된 값으로 초기화됩니다. 물론 catch 블록 안에서는 매개변수의 형태로 선언된 expn에 접근이 가능합니다. 예외상황이 발생여부에 상관없이 이 문장이 실행됨에 주목하세요.
예외상황이 발생했을 때의 실행결과를 보면, try 블록 내에서 예외가 발생하면, catch 블록이 실행이 되고 나서, 예외가 발생한 지점 이후를 실행하는 것이 아니라, catch 블록의 이후가 실행됨을 보입니다. 이렇듯, 예외가 발생하면, 예외가 발생한 지점 이후의 나머지 try 영역은 그냥 건너뛰게 됩니다. throw 절에 의해 던져진 예외 데이터의 자료형과 catch 블록의 매개변수 자료형은 일치해야 합니다. 만약에 일치하지 않으면, 던져진 예외 데이터는 catch 블록으로 전달되지 않습니다.
try 블록을 묶는 기준
try블록을 만나면 그 안에 삽입된 문장이 순서대로 실행됩니다. 예외가 발생하지 않으면 catch 블록 이후를 실행합니다. 예외가 발생하면, 예외가 발생한 지점 이후의 나머지 try 영역은 건너뜁니다. 예외가 발생할만한 영역을 묶는 는 것인가요. try 블록 내에서 예외가 발생하면, 예외가 발생한 지점 이후의 나머지 try 영역은 건너뜁니다.
Stack Unwinding 스택 풀기
예외처리에 대한 책임은 MyFunc을 호출한 영역으로 넘어가게 됩니다.
#include <iostream> using namespace std; void Divide(int num1, int num2) { if (num2 == 0) throw num2; cout << "divide: " << num1 / num2 << endl; cout << "remaning: " << num1%num2 << endl; } int main(void) { int num1, num2; cout << "two number input: "; cin >> num1 >> num2; try { Divide(num1, num2); cout << "Finished" << endl; } catch (int expn) { cout << "dem is " << expn << "no no" << endl; cout << "re pragramming" << endl; } return 0; } |
예외가 처리되지 않으면, 예외가 발생한 함수를 호출한 영역으로 예외 데이터가 (더불어 예외처리에 대한 책임까지) 전달됩니다.
예외 상황이 발생한 위치와 예외 상황을 처리해야 하는 위치가 다른 경우