photoner 2020. 10. 15. 14:32
728x90
반응형

예외처리

  • 예외

프로그램 실행 도중 발생하는 예외적인 상황을 이야기 한다. 컴파일 시 발생하는 에러는 예외와는 관련이 없다. 0으로 나눗셈 등이 예외이다.

 

기존 방식의 if문을 통한 예외 처리

기존 방식의 if문을 통한 예외 처리이다. 이 경우 먼저 중요한 것은 예외가 발생하는 위치와 예외가 발견되는 위치는 다르다는 것을 알아야한다. 그리고 위와 같은 기존 방식의 if문을 통한 예외 처리의 경우 프로그램의 흐름을 구성하는 코드와 예외 처리를 위한 코드를 한 눈에 쉽게 구분하기 힘듦.

 

C++의 예외 처리 메커니즘

먼저, 위와 같이 try의 구역은 예외 발생이 예상 되는 지역을 이야기 한다. try 지역을 선정하는 것은 예외가 발생할 만한 영역 만을 선정하는 것이 아니라 이 예외와 관련된 영역 모두를 구역으로 묶고 선정해야 한다. 예외가 발생함으로써 실행 되서는 안 될 (예외 지역부터 순차적으로 실행되어 왔어야 할) 영역을 묶지 않게 된다면 문제가 되기 때문이다. , 트랜잭션의 개념으로 try 구역을 묶고 선정해야 한다.

다음은 throw 키워드를 이용하여 발생한 예외 상황에 대해서 알릴 수 있고, 알림으로써 처리를 위한 루틴(catch)으로 예외를 전달할 수 있다. 예외를 던졌다고 표현한다. throw를 통해 예외가 알려지면 해당 catch 영역 내의 해당 throw 밑의 영역은 실행하지 않고 예외가 처리되는 루틴으로 실행 흐름이 넘어간다.

마지막으로 catch 영역은 예외를 처리하는 영역이다. throw에 의해서 알려져 전달된 예외를 이 catch 블록에서 처리를 하는데 이 때, 전달된 예외의 자료형과 catch의 매개 변수의 자료형은 일치해야 전달과 처리까지 진행된다. (자료형 일치를 통해서 던져진 예외는 같은 자료형의 매개변수를 지닌 catch로 전달된다.) 이러한 catch 영역은 try에 연이어 구성되어야 하며, 하나의 try구문 당 여러 catch 구문이 구성될 수 있다. 왜냐하면 처리해야 할 예외의 자료형이 여럿일 수 있기 때문이다.

예외가 발생하지 않을 경우 try문을 실행하고, 이어서 등장하는 catch문은 뛰어 넘고 catch문 직후를 실행한다. 그러나 예외가 발생할 경우 throw에 의해 발생된 예외가 던져지고 던져진 이후의 try 문장은 실행하지 않고, 던져진 예외는 해당 예외를 감싸는 try문에 의해 감지가 되고, 이어서 등장하는 catch 블록에 전달되고 처리된다. 그렇게 처리된 후 다시 try문으로 돌아가 나머지 문장을 실행하는 것이 아니라, catch 블록 이후의 라인이 실행된다.

 

  • Throw를 통한 예외의 전달

예외가 throw에 의해 던져진 지역에서 처리가 되지 않는 경우는 함수 내에서 예외가 발생했지만 그에 따른 처리가 함수 내에서 이루어지지 않는 경우와 일맥상통하다. 위의 예제를 보면 divFunc 함수 내에서 throw에 의해 예외가 던져졌지만, 그에 상응하는 예외 처리가 divFunc 함수 지역 내에서 이루어지지 않았는데 이렇게 해당 지역에서 예외 처리가 이루어지지 않을 경우에는 예외가 발생한 함수를 호출한 영역으로 예외와 예외처리에 대한 책임이 전달된다. 위의 예에서 12번 라인에 의해 예외가 던져지면 13, 14번 라인은 진행되지 않고(함수가 종료됨) 흐름은 27번 라인으로 시작된다.

이러한 현상을 스택 풀기라고 한다. 함수 내의 또 다른 함수 호출이 이루어지는 상황에서 예외가 던져지면 함수가 호출된 순서의 반대 순서로 리턴 하듯이 종료하면서 예외가 전달된다.

 

  • 전달되는 예외의 명시

예외가 전달되는 경우가 존재하는 함수에 대해서 전달될 수 있는 예외의 종류를 명시할 수 있다. 이렇게 명시되는 예외의 종류 또한 함수의 특징이 될 수 있는데 이렇게 전달되는 예외의 종류를 명시해야만 하는 이유는 예외가 함수 내에서 처리되지 않고 전달되어 오게 되면 해당 함수 호출 부분 아래에 catch 문을 구성해야 하는데 이 catch문을 어떤 타입의 예외들로 구성을 해 놓아야 하는지를 알 수 있기 때문이다.

위와 같이 예외를 명시할 수 있다.

 

  • 예외 객체/클래스와 상속 관계에서의 catch 순서

  

위의 예제는 클래스 객체를 통해서 예외 클래스를 만들고 활용하는 예제이다. 여기서 중요한 것은 예외 객체의 상속 관계에 있는 예외들을 전달하게 되는데 받을 때 catch문의 구성이 중요하다. 먼저 왼쪽 main 함수 내의 catch 문 구성은 Base 클래스부터 catch 문을 구성하였고 아래로 내려갈수록 Derived 클래스 순으로 구성하는 것을 볼 수 있다. 오른쪽 main 함수 내의 catch 문 구성은 최하위 Derived 클래스부터 아래로 내려갈수록 Base 클래스 순으로 구성을 하였는데 이에 대한 결과는 다음과 같다.

왼쪽 main 함수에서는 전달되어 온 예외들은 모두 첫 번째 catch ExceptionA 클래스가 모두 받는다. 반면에 오른쪽 main 함수에서 전달되어 온 예외들은 정상적으로 각 예외를 적절한 catch문이 받는 것을 확인할 수 있다. 왜냐하면 Base 클래스가 Derived 클래스 형태로 전달되어 온 예외 객체를 모두 받기 때문이다.

  • new 연산자에 의해 발생되는 예외

bad_alloc 예외 클래스는 new 헤더에 만들어져 있고 메모리 공간의 할당이 실패했음을 알리는 의도로 정의된 예외 클래스이다.

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

위와 같이 마지막 catch“…” 을 매개변수로 넣은 catch 문을 확인할 수 있는데 이 때, “…”의 의미는 위에서 거르지 못하고 전달되어 온 예외들을 모두 받아드리겠다는 선언이다. , 위와 같은 선언을 할 시에는 발생하는 예외에 대해서 어떤 정보도 받을 수 없고, 예외의 종류 또한 구분할 수 없다. 

  • 전달되어 온 예외, 다시 던지기!

예외를 이중으로 다시 던지는 것이 가능함을 보여주는 예이다. 하지만 간결한 구조를 위해 굳이 예외를 거듭해서 던질 필요 없다.

728x90
반응형