ABOUT ME

Today
Yesterday
Total
  • [C/C++ 강좌] 93강. 예외 처리
    C++ 2022. 1. 22. 14:39

    https://www.youtube.com/watch?v=qOA315UQHMI&list=PLlJhQXcLQBJqywc5dweQ75GBRubzPxhAk&index=98

     

    1. 예외 처리

    #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;
    		}
    		cout << n << " ! = " << fact(n) << endl;
    	}
    
    	catch (int e) {
    		cout << e << ": 음수입니다." << endl;
    	}
    }

    fact함수는 팩토리얼을 표현한 재귀함수이다. 그런데 팩토리얼은 음수 값이 들어가면 안되므로 예외 처리를 통해서 음수 값을 처리해 줄 수있다.

     

    try는 예외처리의 시작점이다 if 문을 통해 n값이 음수가 들어가게 되면 n이라는 객체를 throw를 통해서 던져버린다.

    그리고 던져진 객체는 catch에서 잡게되어 예외를 통한 처리가 진행된다.

    그리고 throw가 실행되면 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;
    	cin >> n;
    	try {
    		if (n < 0) {
    			throw to_string(n) + ": 음수입니다.";
    		}
    		cout << n << " ! = " << fact(n) << endl;
    	}
    
    	catch (string e) {
    		cout << e << endl;
    	}
    }

     

    그런데 한 가지 의문이든다. 그냥 if문으로 처리해도 될걸 굳이 try catch를 사용하여 가독성도 떨어지고 코드만 복잡해졌다. 그런데 예외처리문에는 한가지 강력한 기능이 있다. 그것은 객체를 던질 때 함수 간에도 던질 수 있다는 것이다.

    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) + ": 음수입니다.";
    		int a = fact(n);
    
    		if (r < 0) 	throw to_string(n) + ": 음수입니다.";
    		int b = fact(r);
    
    		if (n-r < 0) throw to_string(n) + ": 음수입니다.";
    		int c = fact(n - r);
    		cout << n << " ! = " << fact(n) << endl;
    	}
    
    	catch (string e) {
    		cout << e << endl;
    	}
    }

    위 코드는 throw를 if문에서 처리해준다. 그런데 throw를 fact함수에 넣게되면 fact와 main함수 간에 객체를 던지는 행위가 발생한다. 

    #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; // (n C r)조합을 처리하는 공식표현
    	}
    	catch (string e) {
    		cout << e << endl;
    	}
    }

    fact함수 안에서 예외가 발생하면 자신을 호출한 메인함수로 객체를 던지고 catch가 있는 main함수에서 이 던져진 객체를 처리한다.

    코드도 더 간결해보이고 좋다.

     

    #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함수는 comb에서 호출되면 comb함수에서 처리되는지 궁금해진다. 정답은 main함수에서 처리된다. 

    throw를 통해 자신을 호출한 comb함수로 객체를 던지게되고 comb함수에는 try catch문이 없으므로 자신을 호출한 main함수로 객체를 던진다. fact->comb->main식으로 객체는 전달되어서 최종적으로 main함수에 있는 try catch문에서 예외가 처리된다.

     

    그리고 catch는 여러개 존재할 수 있다.

    int main() {
    	int n, r;
    	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은 int형 이므로 123객체는 매개변수가 int형인 catch(int e)로 던져진다.

     

     

    한가지 주의할점은 던져진 객체가 어떤 catch에서도 못받게되면 런타임 에러가 발생한다.

    int main() {
    	int n, r;
    	try {
    		throw 123.1; // 실수형은 123.1을 어떤 catch에서도 받을 수 없기 때문에 런타임에러 발생!
    	}
    	catch (const string &e) {
    		cout << e << endl;
    	}
    	catch (int e) {
    		cout << e << endl;
    	} 
    }

    이럴 때는 아래 catch(...)과 같은 방법을 통해 해결할 수 있다.

     

    int main() {
    	int n, r;
    	try {
    		throw 123.1; // 실수형은 123.1을 어떤 catch에서도 받을 수 없기 때문에 런타임에러 발생!
    	}
    	catch (const string& e) {
    		cout << e << endl;
    	}
    	catch (int e) {
    		cout << e << endl;
    	}
    	catch (...) {
    		cout << "알 수 없는 예외 발생!" << endl;
    	}
    }

     

Designed by Tistory.