[CPP-05] 예외 처리 (exception handling)

C에서는 조건문(if)를 통해서 예외를 처리했었지만, CPP에서는 보다 더 직관적인 문법을 제공한다.

목표 : 공무원 클래스의 등급이 정해진 범위를 벗어났을 때 예외를 발생시킴으로써 CPP의 예외처리 방법에 대해 학습한다.

[Ex00: Mommy, when I grow up, I want to be a bureaucrat! 과제]

  1. Bureaucrat (공무원) 클래스 작성

    • 멤버변수: const name

    • 멤버변수: grade (1~150 사이의 int 범위): 유효하지 않은 grade면 예외처리를 해야한다.

      • Bureaucrat::GradeTooHighException 클래스

      • Bureaucrat::GradeTooLowException. 클래스

    • getName()

    • getGrade()

    • increaseGrade(void) 와 decreaseGrade(void);

      • 1이 높은 등급이고 150이 가장 낮은 등급이다.

  2. 예외처리블록은 다음과 같이 catchable 해야한다.

   try
   {
           /* do some stuff with bureaucrats */
   }
   catch (std::exception & e) 
   {
           /* handle exception */
   }
  1. << 연산자 오버로드

   <name>, bureaucrat grade <grade>

1. try ~ catch 문

이번 과제에서는 CPP에서 예외 처리(exception handling)를 어떤 방법으로 하는지 학습할 수 있다. 예외를 처리한다는 것은, 예외가 발생할 만한 상황예외가 발생했을 때의 동작을 프로그램 구현 단계에서 미리 예상하고 있겠다는 의미다.

C에서는 조건문(if)를 통해서 예외를 처리했었지만, CPP에서는 보다 더 직관적인 문법을 제공한다. trycatch 구문인데, 예외가 발생할 만한 상황try 코드블록에, 예외가 발생했을 때의 동작catch 코드블록에 정의해두면 된다.

  • try : 예외가 발생할 가능성이 있는 코드 블록. 예외가 발생하지 않는다면 try~catch문이 없는 것과 동일하게 동작하며, 예외가 발생하면 그 즉시 catch문으로 점프한다.

  • catch : 내부에 정의된 예외처리를 실시하는 코드 블록.

조건문은 프로그램의 논리를 위한 코드로만 사용하고, 예외를 처리할 때는 '의도치 않은 상황'과 그때의 동작에만 집중해 코드를 짤 수 있도록 프로그램 차원에서 문법을 제공하는 것이다.

그러면 개발자는 '의도치 않은 상황'이 언제 발생할 수 있을지만 생각해 보면 된다. 이번 과제에서는 공무원 클래스의 등급이 1~150 범위를 벗어날 때일 것이다.

  1. 공무원 객체를 생성하면서 인자로 등급을 받을 때.

  2. 생성된 공무원의 등급을 올리거나 내릴 때.

위의 두 상황을 항상 try 코드블럭에 넣어두면, 예외가 발생했을 때 catch절로 넘어가 상황을 종료하고 정상적인 동작을 취할 수 있도록 프로그램을 잘 구현할 수 있게 된다.

2. throw 문

try 에서 예외가 발생했을 때 어떻게 catch 절에 예외 상황을 전달할 수 있을까? 이때 사용하는 것이 throw문이다. throw 는 try 문에서 예외를 탐색하면 throw의 변수를 catch로 전달한다.

try {
    if (예외 상황) {
        throw 변수명
    };
};

catch 는, throw에서 던진 변수를 인자로 받아 catch 내부에 정의된 예외처리를 실시하는 코드 블록이다. 이때 각 catch 절은 인자로 받은 변수의 타입에 따라 예외 상황을 다르게 처리할 수 있다.

하지만 CPP 표준에서는 throw 문의 피연산자로 std::exception에서 파생되는 예외 타입을 사용하도록 권장하고 있다. 이 클래스에는 cpp에서 제공하는 많은 예외 타입이 이미 정의되어 있기 때문이다.

3. std::exeception::what()

예외를 담당하는 기본 CPP제공 클래스이다. 모든 예외 처리 클래스는 이 클래스를 상속받아서 작성된다. 따라서 다형성의 원리에 의해 어떤 에러를 던지더라도, 추상클래스인 exception 클래스 로 받을 수 있다.

exception 클래스는 하나의 문자열 포인터를 반환하는 what()이라는 가상 멤버 함수를 제공한다. 이 멤버 함수는 가상 함수이므로, exception 클래스로부터 파생된 클래스 내에서 재정의 해 원하는 문자열을 출력할 수 있도록 할 수 있다.

what 함수의 레퍼런스와 반환값은 다음과 같다. (출처)

virtual const char* what() const throw();  //(until C++11)
virtual const char* what() const noexcept; //(since C++11)
  • 반환값: Pointer to a null-terminated string with explanatory information.

  • 42 과제는 C++98을 표준으로 하기 때문에 throw()를 사용한다.

4. exeception 예제

1) 헤더파일

#include <exception>

class Bureaucrat
{
private:
    const std::string    name_;
    int                                grade_;

public:
  Bureaucrat(const std::string name, int grade);
    ~Bureaucrat();

    class GradeTooHighException : public std::exception
    {
    public:
        virtual const char* what() const throw();
    };
};
  1. std::exeception의 파생클래스인 GradeTooHighException을 선언했다.

  2. what() 가상함수도 함께 선언한다.

2) 소스파일

#include "Bureaucrat.hpp"

Bureaucrat::Bureaucrat(const std::string name, int grade) : name_(name), grade_(grade)
{
    if (grade_ < 1)
        throw Bureaucrat::GradeTooHighException();
}

const char* Bureaucrat::GradeTooHighException::what() const throw()
{
    return ("BureaucratException: Grade too High");
}
  1. Bureaucrat 객체 생성시 인자로 들어온 grade_가 1보다 작을 때, exeception 클래스를 throw한다.

  2. what()을 오버라이딩한다. 원하는 문자열을 반환하면 된다.

3) main문

#include "Bureaucrat.hpp"

int main(void)
{
    try
    {
        Bureaucrat test("test", 151);
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
    return (0);
}
  1. 예외를 throw 할 것 같은 곳에 try block으로 감싼다.

  2. 예외가 발생했을 경우에만 실행할 코드를 catch block 으로 감싼다.

  3. exception& e 는 프로그램이 던지는 에러를 받아주는 객체이다.

  4. try/catch 구문은 if 문 처럼 state가 한줄이더라도 꼭 {} 로 감싸줘야 한다.

Last updated