예를 들어 다음과 같이 선언했다고 하자.
int main()
{
int a[4];
int b = a[4];
}
초반에 많이 했던 실수다. a[4]라고 선언하면 a[0] ~ a[3] 까지만 인덱스로 접근 가능하다.
컴파일할 때에는 아무런 문제가 없지만 당연히 실행하면 에러가 뜬다. 범위를 초과했기 때문이다.
이렇게 내가 생각한 것과 다른 방식으로 동작을 했을 때, 예외가 발생했다고 한다.
C++에서는 이러한 예외를 처리하기 위한 구문을 제공하고 있다. 하지만 위와 같은 직접 배열에 인덱스로 접근하는 경우는 처리가 불가능 하다... 이유는 배열 인덱스는 함수가 아니라서 내가 끼어들 수가 없다. 다음에서 설명할 throw문을 집어넣을 수 없기 때문이다.
예외 발생시 어떠한 디버그용 메세지를 전달할 수 있는데 이때 이용되는 것이 바로 throw이다.
throw
함수 어디에서든 throw (특정 변수); 를 써줄 수 있다.
다음과 같이 쓸 수 있다.
void HeyINeedNumber1(int a)
{
if (a != 1)
{
throw std::string("1만 달라고 했잖아 ㅡㅡ");
}
}
그러면 throw된 변수는 어떻게 될까?
이 throw 된 녀석을 처리해주기 위한 구문이 바로 try ~ catch 이다.
try ~ catch
이건 설명하는 것보다 예시를 한번 보는 게 빠를 것 같다.
int main()
{
try {
HeyINeedNumber1(3);
}
catch (std::string & str)
{
cout << str << endl;
}
}
throw를 하는 함수들을 try 내부에 넣어두면 정확한 동작방식은 모르겠지만 대충 이해해보자면 throw된 변수들을 try에서 수집한 후 catch (변수) 에게 전달해준다. catch 문에는 반드시 어떤 타입의 변수를 처리할 것인지를 알려줘야 한다.
예를 들어 catch(){ ...} 이렇게 해주면 안된다는 얘기다. 변수 타입이 여러가지라면 catch문도 여러 개 넣어줄 수 있다. switch 문의 case 처럼 생각하면 이해가 편할 것이다.
여러 개의 catch 문은 다음과 같이 쓸 수 있다.
void HeyINeedNumber1(int a)
{
if (a != 1)
{
throw std::string("1만 달라고 했잖아 ㅡㅡ");
}
throw a;
}
int main()
{
try {
HeyINeedNumber1(1);
}
catch (std::string & str)
{
cout << str << endl;
}
catch (int num)
{
cout << "ㄳㄳ" << endl;
}
}
이렇게 하면 "ㄳㄳ" 가 출력 된다. 숫자가 1일 경우에 throw로 정수형 변수를 방출해줬기 때문이다.
아, 그리고 throw가 되고 나면 마치 return 처럼 다음 줄부터는 동작하지 않는다.
근데 모든 타입에 대해서 처리하고 싶은데 일일히 다 써주기엔 너무 힘들다. 그래서 ... 연산자도 지원한다. 모든 예외를 하나의 catch문에서 받고 싶을 때는
catch(...)
{
}
요런 식으로 써주면 된다.
일단 throw / try / catch 에 대한 개념은 이 정도면 될 것 같다.
다음으로 표준 라이브러리에 있는 std::exception에 대해 알아보자.
그냥 std(Standard Library) 에 있는 것들의 예외처리를 위해 따로 만들어준 클래스이다.
이 클래스가 가진 의의라고 한다면 다형성도 있고 예외처리 관련 다른 객체나 함수들을 이용할 수 있기 때문이다. 만약 여러가지의 에러를 한번에 처리를 하고 싶은데 catch문을 무한정 만들 수도 없는 노릇이기 때문에 catch문에는 그냥 std::exception& e 이것만 받도록 만들어 놓고
- bad_optional_access(C++17)
- runtime_error
- range_error
- overflow_error
- underflow_error
- regex_error(C++11)
- system_error(C++11)
- ios_base::failure(C++11)
- filesystem::filesystem_error(C++17)
- tx_exception(TM TS)
- nonexistent_local_time(C++20)
- ambiguous_local_time(C++20)
- format_error(C++20)
- bad_any_cast(C++17)
- bad_weak_ptr(C++11)
- bad_function_call(C++11)
- bad_alloc
- bad_array_new_length(C++11)
- bad_exception
- ios_base::failure(until C++11)
- bad_variant_access(C++17)
이런 식으로 std::exception을 상속받는 클래스들을 왕왕 만들어두고 throw로 해당 클래스를 방출하는 방식이다.
그래서 예외처리를 하려고 한다면 std::exception을 상속받는 클래스를 만들어서 사용하는 것이 좋다고 한다.
'Programming' 카테고리의 다른 글
[C++] constexpr (1) | 2019.12.19 |
---|---|
[C++] noexcept 선언 (1) | 2019.12.19 |
C++에서 C# Style Property 사용하기 (1) | 2019.12.18 |
멀티쓰레딩 연습 (0) | 2019.12.18 |
Enum class 에 대한 한가지 사실 (0) | 2019.12.16 |