문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C++ - Stack Unwinding(스택 풀기) (예외의 전달, 예외상황이 발생한 위치와 예외상황을 처리해야 하는 위치가 다른 경우, 스택 풀기, 자료형이 일치하지 않아도 예외 데이..

벼리네 2021. 7. 1. 12:08
반응형

[문과 코린이의 IT 기록장] C++ - Stack Unwinding(스택 풀기) (예외의 전달, 예외상황이 발생한 위치와 예외상황을 처리해야 하는 위치가 다른 경우, 스택 풀기, 자료형이 일치하지 않아도 예외 데이터는 전달됨, 하나의 try 블록과 다수의 catch 블록, 전달되는 예외의 명시)

[문과 코린이의 IT 기록장] C++ - Stack Unwinding(스택 풀기) (예외의 전달, 예외상황이 발생한 위치와 예외상황을 처리해야 하는 위치가 다른 경우, 스택 풀기, 자료형이 일치하지 않아도 예외 데이터는 전달됨, 하나의 try 블록과 다수의 catch 블록, 전달되는 예외의 명시)

 


2021.07.01 - [문과 코린이의, [C. C++] 기록/C++ 이론] - [문과 코린이의 IT 기록장] C++ - 예외상황과 예외처리의 이해 및 예외처리 매커니즘 (예외상황을 처리하지 않았을 때의 결과, if문을 이용한 예외처리, C++의 예외처리 매커니즘의 이해 : try와 cat..

 

[문과 코린이의 IT 기록장] C++ - 예외상황과 예외처리의 이해 및 예외처리 매커니즘 (예외상황을

[문과 코린이의 IT 기록장] C++ - 예외상황과 예외처리의 이해 및 예외처리 매커니즘 (예외상황을 처리하지 않았을 때의 결과, if문을 이용한 예외처리, C++의 예외처리 매커니즘의

vansoft1215.tistory.com

 


1. 예외의 전달

- throw절에 의해 예외가 발생하긴 했는데, 이를 처리하지 않는다면 어떻게 될까?

 

Case 1 )

#include<iostream>
using namespace std;

void Divide(int num1, int num2) {
	if (num2==0)
	{
		throw num2; // 함수 내부에 throw가 존재하는데, try-catch문이 없는경우?
	}
	cout << "나눗셈의 몫 : " << num1 / num2 << endl;
	cout << "나눗셈의 나머지 : " << num1 % num2 << endl;
}

int main() {
	int num1, num2;
	cout << "두 개의 숫자 입력 : ";
	cin >> num1 >> num2;

	try
	{
		Divide(num1, num2); // 함수에서 발생한, 예외처리에 대한 책임이 넘어옴
		cout << "나눗셈을 마쳤습니다." << endl;
	}
	catch (int expn) // 20행에서 예외처리가 발생하면, 실행됨
	{
		cout << "제수는 " << expn << "이 될 수 없습니다." << endl;
		cout << "프로그램을 다시 실행하세요." << endl;
	}
	return 0;
}

 

 

※ 정리
- 예외가 처리되지 않으면, 예외가 발생한 함수를 호출한 영역으로 예외 데이터가(더불어 예외처리에 대한 책임까지) 전달된다.

* 이러한 특성은 예외가 발생한 위치와, 예외가 처리되어야 하는 위치가 달라야만 하는 경우에 유용하게 활용되는 특성이다.

 


2. 예외상황이 발생한 위치와 예외상황을 처리해야 하는 위치가 다른 경우

- 대부분의 경우에 있어 예외의 발생위치와 처리우치는 다르지만, 대표적인 예를 하나 이야기해보고자 한다.

 

Case 2 )

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;

int StoI(char* str) {
	int len = strlen(str);
	int num = 0;

	for (int i = 0; i < len; i++) {
		if (str[i] < '0' || str[i]>'9') {
			throw str[i]; // 실행되면 뒷부분의 함수 코드는 더이상 실행되지 않음
		}
		num += (int)(pow((double)10, (len - 1) - i) * (str[i] + (7 - '7')));
		// pow(a,b) : a의 b제곱
		// str[i]+(7-'7') 아스키코드로 생각하기 꼭 7이 아니라 어떤 숫자도 가능
	}
	return num;
}

int main() {
	char str1[100];
	char str2[200];

	while (1) {
		cout << "두 개의 숫자 입력 : ";
		cin >> str1 >> str2;

		try
		{
			cout << str1 << " + " << str2 << " = " << StoI(str1) + StoI(str2) << endl;
			// Stoi(str) : 정수로 변환하는 과정
			break;
		}
		catch (char ch)
		{
			cout << "문자 " << ch << "가 입력되었습니다." << endl;
			cout << "재입력 진행합니다." << endl << endl;
		}
	}
	cout << "프로그램을 종료합니다." << endl;
	return 0;
}

 

 


3. 스택 풀기 (Stack Unwinding)

- 예외가 처리되지 않아서, 함수를 호출한 영역으로 예외 데이터가 전달되는 현상을 가리켜 '스택 풀기'라고 한다.

 

Case 3 )

#include<iostream>
using namespace std;

void SimpleFuncOne();
void SimpleFuncTwo();
void SimpleFuncThree();

int main() {
	try
	{
		SimpleFuncOne();
	}
	catch (int expn)
	{
		cout << "예외코드 : " << expn << endl;
	}
	return 0;
}

void SimpleFuncOne() {
	cout << "SimpleFuncOne()" << endl;
	SimpleFuncTwo();
}

void SimpleFuncTwo() {
	cout << "SimpleFuncTwo()" << endl;
	SimpleFuncThree();
}

void SimpleFuncThree() {
	cout << "SimpleFuncThree()" << endl;
	throw - 1; // 예외 데이터의 발생
}

 

 

처리되지 않은 예외의 전달과정
스택이 풀리는 과정 

* 만약 앞의 예제의 main함수에 있는 try-catch문을 삭제하면?

: 예외가 처리되지 않아서 main함수까지 도달했는데도, main함수에서도 예외를 처리하지 못한다면, terminate함수(프로그램을 종료시키는 함수)가 호출되면서 프로그램이 종료되어 버린다.

 

- 따라서 시스템 오류로 인해 발생한 예외상황이 아니라면, 즉 더 이상 프로그램의 실행이 불가능한 예외상황이 아니라면, 반드시 프로그래머가 예외상황을 처리해야 한다.

 


4. 자료형이 일치하지 않아도 예외 데이터는 전달된다.

만약, 데이터의 자료형과 catch의 자료형이 일치하지 않는다면?

int SimpleFunc(){
	...
    try
    {
    	if( ... )
        	throw -1; // int형 예외 데이터의 발생
    }
    catch(char expn) { ... } // char형 예외 데이터를 전달하라
	...
}

이 경우 자료의 불일치로 인해 예외는 처리되지 않는다. 즉, catch블록으로 값이 전달되지 않으므로, SimpleFunc()를 호출한 영역으로 예외 데이터가 전달된다.

 


5. 하나의 try 블록과 다수의 catch 블록

하나의 try 블록 내에서 유형이 다른 둘 이상의 예외상황 발생 가능.

- 이러한 경우에는 각각의 예뢰르 표현하기 위해 사용되는 예외 데이터의 자료형이 다를 수 있음.

- 따라서, try 블록에 이어서 등장하는 catch블록은 둘 이상이 될 수 있음

 

Case 4 )

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;

int StoI(char* str) {
	int len = strlen(str);
	int num = 0;

	// 0으로 시작하는 수의 입력에 예외를 발생시킴.
	if(len != 0 && str[0] == '0') { 
		throw 0; // 예외 데이터 0 전달
	}

	for (int i = 0; i < len; i++) {
		if (str[i] < '0' || str[i]>'9') {
			throw str[i];
		}
		num += (int)(pow((double)10, (len - 1) - i) * (str[i] + (7 - '7')));
	}

	return num;
}

int main() {
	char str1[100];
	char str2[200];

	while (1) {
		cout << "두 개의 숫자 입력 : ";
		cin >> str1 >> str2;

		try
		{
			cout << str1 << " + " << str2 << " = " << StoI(str1) + StoI(str2) << endl;
			break;
		}
		// char형 예외 데이터 처리
		catch (char ch)
		{
			cout << "문자 " << ch << "가 입력되었습니다." << endl;
			cout << "재입력 진행합니다." << endl << endl;
		}
		// int형 예외 데이터 처리
		catch (int expn) {
			// 0으로 시작하는 수의 입력에 대한 예외처리
			if (expn == 0) {
				cout << "0으로 시작하는 숫자는 입력불가." << endl;
			}
			else {
				cout << "비정상적 입력이 이루어졌습니다." << endl;
			}
			cout << "재입력 진행합니다." << endl << endl;
		}
	}
	cout << "프로그램을 종료합니다." << endl;
	return 0;
}

 

 


6. 전달되는 예외의 명시

함수 내에서 발생할 수 있는 예외의 종류도 함수의 특징으로 간주된다.

- 따라서, 이미 정의된 특정 함수의 호출을 위해서는, 함수의 이름 / 매개변수 선언 / 반환형 정보 + 함수 내에서 전달될 수 있는 예외의 종류(예외 데이터의 자료형)과 그 상황도 알아야 한다.

- 그래야 해당 함수의 호출문장을 감싸는 적절한 try-catch블록을 구성할 수 있기 때문이다.

 

따라서 함수를 정의할 때함수 내에서 발생가능한 예외의 종류를 다음과 같이 명시해주는 것이 좋다.

int ThrowFunc(int num) throw(int, char)
// throw(int, char) : 함수 내에서 예외상황의 발생으로 인해, int형과 char형 예외 데이터가 전달될 수 있음을 의미
// cf ) throw() : 어떠한 예외도 전달하지 않는다. (위 함수가 예외를 전달할 경우 프로그램은 종료가 된다)
{
	...
}

(중략)

int main()
{
	...
    try
    {
    	...
        ThrowFunc(20);
        ...
    }
    catch(int expn) { ... }
    catch(char expn) { ... }
}

 

 


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형