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

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

> _목표 : 공무원 클래스의 등급이 정해진 범위를 벗어났을 때 예외를 발생시킴으로써 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** 해야한다.

```cpp
   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에서는 보다 더 직관적인 문법을 제공한다. `try`와 `catch` 구문인데, **예외가 발생할 만한 상황** 을 `try` 코드블록에, **예외가 발생했을 때의 동작** 을 `catch` 코드블록에 정의해두면 된다.

* `try` : 예외가 발생할 가능성이 있는 코드 블록. 예외가 발생하지 않는다면 try\~catch문이 없는 것과 동일하게 동작하며, 예외가 발생하면 그 즉시 `catch`문으로 점프한다.
* `catch` : 내부에 정의된 예외처리를 실시하는 코드 블록.

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

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

1. 공무원 객체를 생성하면서 인자로 등급을 받을 때.
2. 생성된 공무원의 등급을 올리거나 내릴 때.

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

## 2. throw 문

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

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

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

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

## 3. std::exeception::what()

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

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

`what` 함수의 레퍼런스와 반환값은 다음과 같다. ([출처](https://en.cppreference.com/w/cpp/error/exception/what))

```cpp
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.&#x20;
* 42 과제는 C++98을 표준으로 하기 때문에 throw()를 사용한다.

## 4. exeception 예제

### 1) 헤더파일

```cpp
#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) 소스파일

```cpp
#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문

```cpp
#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으로 감싼다.&#x20;
2. 예외가 발생했을 경우에만 실행할 코드를 `catch` block 으로 감싼다.
3. `exception& e` 는 프로그램이 던지는 에러를 받아주는 **객체**이다.
4. `try/catch` 구문은 `if` 문 처럼 state가 한줄이더라도 꼭 `{}` 로 감싸줘야 한다.
