본문 바로가기

Language/C++

[C++] 예외 처리(Exception Handling) - 컴도리돌이

728x90

outline

C++ Exceptions : Basics

try, catch, and throw

Matching Catch Handlers

Uncaught Exceptions

Cleaning Up

Unwinding the stack


C++ 예외처리 (C++ Exceptions : Basics)

  • 예외 처리(Exception Handling)란 프로그램 실행 도중에 일어나는 비정상적인 상황이 벌어질 때, 이를 처리하는 과정을 말한다.

#include <iostream>
using namespace std;
double Division(int a, int b)
{
  if (b == 0) {throw "Division by zero condition!";}
  return (a / (double) b);
}

int main ()
{
 int x, y;
 double z;
 cin >> x >> y;
 try
 {
   z = Division(x, y);
   cout << z << endl;
 }
 catch (const char* msg) { cerr << msg << endl;}
 return 0;
}
  • 위에 예시에서 Division이라는 함수가 있다 가정하면 y의 값이 0이 아니면 정상적으로 프로그램이 돌아가지만 y의 값이 0이면 throw에서 예외가 발생이 나면서 catch 함수로 예외 메시지를 보낸다.

  • 예외 처리를 하고 싶은 코드는 모두 try에 작성하면 된다.

  • catch 함수에서 예외 출력 값은 char형 또는 int형 또는 커다란 object를 출력시킬 수 있다.

  • 예외 처리가 끝나면 try-catch 함수에 나가게 되고 다음 코드로 넘어간다.

  • 예외 처리가 발생하여 throw로 가면 더 이상 throw가 있는 해당 함수의 다음 코드는 실행되지 않고 catch 함수로 넘어간다.


try, catch, and throw

try{ // 예외를 처리하고 싶은 영역
   if(예외 조건) throw 예외 객체; //예외가 발생하면 catch문으로 들어간다.
} catch (예외 객체) {
   //예외 처리 영역
}

1. try

  • try 블록은 예외 발생에 대한 검사의 범위를 지정할 때 사용된다. 즉 try 블록 내에서 예외가 발생하면, C++의 예외 처리 메커니즘에 의해서 처리가 된다.

try
 {
   z = Division(x, y);
   cout << z << endl;
 }
  • try 안에서 무언가 예외가 발생할만한 코드를 작성한다. 만약에 예외가 발생하지 않는다면 try-catch문이 없는 것과 동일하게 실행된다. 반면에 예외가 발생할 경우, stack에 생성된 모든 객체들의 소멸자들이 호출되고, 가장 가까운 catch 문으로 점프를 한다.

  • 위에 예시를 사용한다면 y의 값에 따라서 예외가 발생할 것이다. 다음 예시에서 확인해보자.

2. throw

  • throw는 예외가 발생했음을 알리는 문장의 구성에 사용된다.

double Division(int a, int b)
{
  if (b == 0) {throw "Division by zero condition!";}
  return (a / (double) b);
}
  • throw 함수는 try 자체에 만들어도 되고, try 안에 있는 함수 안에 작성해도 된다.

  • 예시와 같이 b의 값이 0이면 예외가 발생하는데, throw 안에 예외 객체는 string, int, char, object, 등을 작성할 수 있다.

3. catch

  • catch 블록은 try 블록에서 발생한 예외를 처리하는 코드가 담기는 영역으로써, 그 형태가 마치 반환형이 없는 함수와 유사하다.

 catch (const char* msg) { cerr << msg << endl;}
  • throw에서 string으로 보냈기 때문에 catch문에서는 string을 출력할 수 있게 설정하였다.

  • 예외가 발생하면 throw에 넘어온 예외 객체를 통해 사용에 맞게 예외 출력을 해주면 된다.

  • catch이 실행되면 해당 try-catch문은 종료된다.


예외 타입 일치(Matching Catch Handlers)

  • 예외의 타입은 어떤 핸들러가 그것을 catch 할 수 있는지를 결정한다. 예외들을 위한 matching 규칙들은 함수 오버 로딩을 위한 matching 규칙들 보다 더 한정적이다.

try {
	// some code that may throw an exception
}
catch(T1 t1) {
	// processing for type T1
}
catch(T2 t2) {
	// processing for type T2
}

 

예외 타입 처리

#include <iostream>
#include <string>

using namespace std;

double Division(int a, int b) {
  if (b == 0) {
  throw -1; // "catch int"
  // throw "exception"; // "catch const char*"
  // throw string("exception"); // "catch string&"
  }
  return (a / (double) b);
}

int main () {

  int x, y;
  cin >> x >> y;
  
  try {
  double z = Division(x, y);
  cout << z << endl;
  }
  catch (int e) { cout << "catch int " << e << endl;}
  catch (const char* e) {cout << "catch const char* " << e << endl;}
  catch (string& e) {cout << "catch string& " << e << endl;}
  return 0;
}
  • 위에 예시를 보면 Division 함수 안에 throw의 예외 객체가 int형인 -1의 값을 catch문으로 전달된다. 해당 예외 객체가 int형이기 때문에 int형을 인자로 받는 catch함수가 실행된다.


포착되지 않은 예외(uncaught Exceptions)

  • try Block 밖에서 발생되거나, 어떠한 catch Handler와도 매치되지 않은 예외가 발생된 경우를 말한다.

  • uncaught 예외가 포착되면 terminate() 함수를 호출하며, 프로그램을 즉각 중단시키지 않는다.

void ThrowsException() {
 	throw string("Exception!");
}
void CallsOne() {
 	ThrowsException();
}
void CallsTwo() {
   try {
   	CallsOne();
   } catch (const char* e) {
   	cout << "Caught in CallsTwo";
   }
}

int main() {
   try {
   	CallsTwo();
   }
   catch (string e) {
   	cout << "Caught an exception in main";
   }
   return 0;
}
Output:
Caught an exception in main
  • 위에 예시를 보면 CallsTwo -> CallsOne() -> ThrowsException() 순으로 함수가 실행되는데, ThrowsException() 함수에서 throw으로 인해 예외 객체인 string이 가장 앞에 있는 CallTwo() 함수에 있는 catch문으로 전달되는데 catch문으로 점프를 했지만 해당 catch문 char형의 인자를 받기 때문에 다음 catch문으로 다시 jump를 하게 된다.


스택 해제(winding the stack)

 

  • 위에 그림에서는 예외가 throw 되면 어떻게 스택이 해제되는지 보여준다. 예외는 cfunct() 함수에서 발생했지만 예외의 처리는 main 함수에서 이뤄지는 형태이다. 예외 데이터가 전달되면, 예외 데이터를 전달한 함수는 종료되기 때문에, 예외 데이터를 전달한 함수의 스택이 반환되는 것은 당연하다.
  • 예외 데이터의 전달을 가리켜서 스택 해제(stack unwinding) 또는 스택의 반환이라고 한다.