예외 처리(Exception Handling)
예외 Case
ex) 파일이 제대로 열리지 않는 경우
ex) 데이터 포맷이 맞지 않아 데이터를 받을 수 없는 경우
Factorial을 구하는 함수
#include <iostream>
using namespace std;
int fact(int n) {
if (n == 0) return 1;
return n * fact(n - 1);
}
int main() {
int n;
cin >> n;
if (n < 0) {
cout << n << ": 음수입니다." << endl;
}
else {
cout << n << "!= " << fact(n) << endl;
}
}
이렇게 사용자가 실수로 음수를 입력할 수 있다.
n이 음수인 경우 fact함수가 실행되지 않도록 처리를 해줘야 함
#include <iostream>
using namespace std;
int fact(int n) {
if (n == 0) return 1;
return n * fact(n - 1);
}
int main() {
int n;
cin >> n;
try {
if (n < 0) {
throw n; // n은 예외 객체
}
cout << n << "! = " << fact(n) << endl;
}
catch (int e) {
cout << e << ": 음수입니다. " << endl;
}
// ...
}
예외를 발생할 수 있는 부분을 try 블럭에 넣자
예외가 발생하지 않는다면 cout << n << "! = " << fact(n) << endl; 을 출력하고 catch문 아래로 간다
예외가 발생하면 throw 후 catch문으로 이동하고, throw한 예외 객체를 catch문의 매개변수로 전달하여 catch문 내의 코드를 실행한다
예외에 해당하는 메시지를 만들어서 던져보자
int main() {
int n;
cin >> n;
try {
if (n < 0) {
throw to_string(n) + ": 음수입니다."; // n은 예외 객체
}
cout << n << "! = " << fact(n) << endl;
}
catch (string e) {
cout << e << endl;
}
// ...
}
- 여기서 효율을 생각한다면 예외 객체가 이동 생성이 되도록 해줄 수 있다.
int main() {
/* 생략 */
catch (const string &e) {
cout << e << endl;
}
// ...
}
- if문을 사용하지 않고 try-catch를 사용해야 하는 이유?
#include <iostream>
#include <string>
using namespace std;
int fact(int n) {
if (n == 0) return 1;
return n * fact(n - 1);
}
int main() {
int n, r;
cin >> n >> r;
try {
if (n < 0) {
throw to_string(n) + ": 음수입니다."; // n은 예외 객체
}
int a = fact(n);
if (r < 0) {
throw to_string(r) + ": 음수입니다."; // n은 예외 객체
}
int b = fact(r);
if (n - r < 0) {
throw to_string(n - r) + ": 음수입니다."; // n은 예외 객체
}
int c = fact(n - r);
cout << a / b / c << endl;
}
catch (const string &e) {
cout << e << endl;
}
// ...
}
이렇게 factorial을 호출해줄 때마다 throw를 해줘야 한다면 그냥 if문을 사용하는 거에 비해 장점이 하나도 없다
객체를 던지고 받는 과정은 함수 내에서만 일어나는게 아니라, 함수 사이에서도 일어날 수 있다.
#include <iostream>
#include <string>
using namespace std;
int fact(int n) {
if (n < 0) throw to_string(n) + ": 음수입니다.";
if (n == 0) return 1;
return n * fact(n - 1);
}
int main() {
int n, r;
cin >> n >> r;
try {
int a = fact(n);
int b = fact(r);
int c = fact(n - r);
cout << a / b / c << endl; // 조합(Combination)
}
catch (const string &e) {
cout << e << endl;
}
// ...
}
fact 함수에서는 예외 객체를 던져주기만 하고, 예외 처리를 의무를 메인 함수에 넘긴다
이렇게 처리 해주면 코드가 되게 간결해진다
조합을 구하는 과정을 comb 함수로 모듈화해보자
#include <iostream>
#include <string>
using namespace std;
int fact(int n) {
if (n < 0) throw to_string(n) + ": 음수입니다.";
if (n == 0) return 1;
return n * fact(n - 1);
}
int comb(int n, int r) {
int a = fact(n);
int b = fact(r);
int c = fact(n - r);
return a / b / c;
}
int main() {
int n, r;
cin >> n >> r;
try {
cout << comb(n, r) << endl;
}
catch (const string &e) {
cout << e << endl;
}
// ...
}
조합을 구하는 과정에서 fact()에서 예외가 발생하면 fact() try-catch가 없으므로 comb()로 나오고, comb()에도 try-catch가 없으므로 main으로 나오게 된다. main에 try-catch가 있으므로 main에서 예외처리를 해준다
조합을 여러번 사용자가 구할 수 있도록 while문에 넣어보자
while문 안에 try-catch를 넣는 방법과
try-catch 안에 while문을 넣는 방법이 있다
두 방법의 차이점에 대해서 확인해보자
int main() {
int n, r;
try {
while (true) {
cin >> n >> r;
cout << comb(n, r) << endl;
}
}
catch (const string &e) {
cout << e << endl;
}
// ...
}
try 안에 while문이 있는 경우 while문을 돌 다 예외가 발생하면 while문 밖으로 바로 나가게 된다.
- 예외가 한 번이라도 발생하면 루프를 빠져나온다
int main() {
int n, r;
while (true) {
try {
cin >> n >> r;
cout << comb(n, r) << endl;
}
catch (const string &e) {
cout << e << endl;
}
}
// ...
}
while문 안에 try-catch가 존재하는 경우 예외가 발생해도 while문이 끝나지 않고 계속 루프를 돈다
- 예외가 발생하더라도 루프를 빠져나오지 않는다
catch를 여러 개를 만들 수 있다.
- 예외를 다루는 클래스를 여러 개를 만들고 예외를 계층적으로 다룰 때, 보통 catch를 여러 개를 만들어 사용한다
int main() {
int n, r;
while (true) {
try {
throw 123;
cin >> n >> r;
cout << comb(n, r) << endl;
}
catch (const string &e) {
cout << e << endl;
}
catch (int e) {
cout << e << endl;
}
}
// ...
}
프로그램을 실행하면 throw 123을 먼저 만나서 catch (int e)로 진입하게 된다
catch에서 잡을 수 없는 에러가 발생한다면?
- 위 예시에서 double을 throw한다면?
int main() {
int n, r;
while (true) {
try {
throw 12.3;
cin >> n >> r;
cout << comb(n, r) << endl;
}
catch (const string &e) {
cout << e << endl;
}
catch (int e) {
cout << e << endl;
}
}
// ...
}
catch 안으로 들어가지 않아 런타임 에러가 발생한다
다음과 같이 처리할 수도 있다.
int main() {
int n, r;
while (true) {
try {
throw 12.3;
cin >> n >> r;
cout << comb(n, r) << endl;
}
catch (const string &e) {
cout << e << endl;
}
catch (int e) {
cout << e << endl;
}
catch (...) {
cout << "알 수 없는 예외 발생" << endl;
}
}
// ...
}
- catch하지 못한다면 swtich문의 default 처럼 catch(...) 으로 진입한다
참조) 두들낙서 C/C++ 강좌
'Programming Language > C++' 카테고리의 다른 글
[Modern C++] 함수 객체와 람다식 (0) | 2024.06.28 |
---|---|
[C/C++] 함수 포인터 (0) | 2024.06.28 |
[C++] 템플릿 특수화와 비타입 파라미터 (0) | 2024.06.28 |
[C++] 함수 템플릿과 클래스 템플릿 (0) | 2024.06.28 |
[C++] 정사각형-직사각형 문제 (0) | 2024.06.28 |