본문 바로가기

Archived(CSE Programming)/cpp

Chap 15. 예외처리(Exception Handling)

Chap 15. 예외처리(Exception Handling)


15-1. 예외상황과 예외처리의 이해


예외상황이란 말 그대로 예기치 못한 상황인데 구체적으로 들어간다면, 프로그램 실행 중에 발생하는 예기치 못한 상황이다. 따라서 컴파일 오류와 구분되는 것으로 0으로 나눗셈을 처리하게 된다든지 나이를 입력하랬는데 음수를 입력한다든지 이러한 프로그래머가 설계한 상황과 맞지 않은 상황을 보고 예외라고 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
void main (){
    
    int age;    
 
    cout<<"나이를 입력하세요 : ";
    cin>>age;
    
    // 예외상황 발견
    if (age<0){
        cout<<"나이는 0 이상으로 입력하셔야 합니다. 프로그램을 재실행해주세요"<<endl;
        exit(1);
    }
}
cs


그리고 이러한 예외상황에 대해서 처리하는 것을 보고 예외처리라고 한다.

여기서는 if 문을 통해 예외 상황을 점검하고 그리고 경고문과 프로그램을 종료하는 것으로 예외처리를 진행하였다.


15-2. c++의 예외처리 메커니즘


위와 같이 if 문으로 예외처리를 할 수 있지만 프로그램의 실제 실행 흐름과 예외처리문이 구분이 가지 않을 수 있다. 따라서 c++에서는 try~catch, throw 이 3가지 키워드를 통해 예외상황을 처리한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main (){
    
    int age;    
    
    try{
        cout<<"나이를 입력하세요 : ";
        cin>>age;
        
        if(age<0)
            throw age;
    }
    
    catch(int age){
        cout<<"음수 입력, 프로그램 종료합니다"<<endl;
        exit(1);
    }
}
cs


기본적인 틀은 try 에서 예외상황이 발생할 수 있는 프로그램의 실행 흐름을 포함하고, 예외 상황이 발생할 시, throw를 통해 예외를 넘겨주고 catch에서 예외를 처리해주도록 한다.


여기서 try 블록은 예외상황 발생시 나머지 부분을 실행하지 않기 때문에 단순히 나이 입력 부분만 try로 묶는 것이 아니라 나이를 입력받은 이후에 실행해야될 부분 모두를 try 영역 안에 포함시켜주어야 한다. 또한 catch로 예외를 처리하더라도 프로그램이 종료되지 않았다면 catch 이후의 문장들이 실행될 수 있기에 try~catch, throw 구문의 구성에 대해서 고려를 하여야 한다.


15-3. Stack Unwinding(스택 풀기)


예외는 기본적으로 처리가 되지않으면 던져진다.

만약 호출된 함수에서 예외가 발생하고 이를 처리되지 않으면 해당 함수를 호출한 상위 영역으로 예외가 던져지고 이렇게 계속해서 처리가 되어질 때까지 던져지다가 main 함수에서도 이를 처리하지 않으면 프로그램이 종료된다.


이렇게 스택으로 쌓여있던 함수 호출 부분들이 예외 발생시 스택에서 풀리게 되고 이러한 상황을 보고 stack unwinding 이라고 표현을 하는 것이다.


추가적으로 자료형이 일치하지 않더라도 예외 자체는 전달이 되고, 하나의 try 블럭에 대해 여러 개의 catch 문 구성도 가능하다.

마지막으로 다음의 양식과 같이 어떠한 매개 타입의 예외를 발생시키는지 명시하여 이 이외의 예외 발생 시 문제가 되도록 예외처리를 구성해줄 수도 있다.


1
2
3
int throwFunc(voidthrow (intchar){
...
}
cs


15-4. 예외 상황을 표현하는 예외 클래스의 설계


예외를 위와 같은 기본 자료형으로도 처리할 수 있지만 이러한 자료형으로는 예외 정보를 알리는 것에 한계가 있다. 그래서 우리는 예외 객체를 설계하여 예외의 구체적인 상황에 대해 명시를 할 수 있고, 뿐만 아니라 예외 클래스를 상속하여 상위 클래스 예외로 묶어서 어떠한 예외든 한번에 처리할 수 있도록 구성도 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 
// 입금 예외 클래스
class DepositException{
private:
    int reqDep;
public:
    DepositException(int r):reqDep(r){}
    void showException(){
        cout<<reqDep<<"해당 금액을 입금할 수 없습니다"<<endl;
    }
};
 
// 출금 예외 클래스
class WithdrawException{
private:
    int reqWit;
public:
    WithdrawException(int w):witDep(r){}
    void showException(){
        cout<<witDep<<"해당 금액을 출금할 수 없습니다"<<endl;
    }
};
 
class Accout{
private:
    char accNum[50]; //계좌번호
    int balance; // 잔고
 
public:
    Accout(char * acc, int money):balance(money){
        strcpy(accNum,acc);
    }
    // 입금 함수 (입금예외 클래스 명시)
    void deposit(int m) throw (DepositException){
        ... // 입금 과정 생략
    }
    // 출금 함수 (출금예외 클래스 명시)
    void withdraw(int m) throw (WithdrawException){
        ... // 출금 과정 생략
    }
    ...
 
};
 
void main(){
    Account myAcc("1234-1234",5000);
    
    try{
        myAcc.deposit(-300);
    }
    catch(DepositException & expn){
        expn.showException();
    }
    
    try{
        myAcc.withdraw(500000);
    }
    catch(WithdrawException & expn){
        expn.showException();
    }
}
 
cs


15-5. 예외 처리와 관련된 또 다른 특성들


new 로 메모리 할당 시 문제가 생기면 bad_alloc 이라는 예외상황이 자동으로 발생한다. throw로 명시하지 않아도 발생한다. 

그리고 catch 블럭 내에서도 throw로 예외 상황을 던질 수 있다.