[C++ 정리] 예외처리

프로그램은 ‘버그(bug)’ / ‘오류(error)’ / ‘예외(exception)’에 의해서 멈추거나 종료되기도 한다. 세가지 단어를 구분하여 보자.

사전적인 의미

* bug : (컴퓨터 시스템이나 프로그램의)오류
* error : 오류
* exception : 예외

어느 저자의 정의

* bug : 프로그래머에 의한 프로그램 오작동 문제
  ※ NULL포인터 호출, 배열 오버플로우, 동적메모리 미삭제로 인한 메모리 누수 등
* error : 사용자에 의한 프로그램 오작동 문제
  ※ 할당된 크기보다 큰 문자열을 입력
* exception : 예상치 못한 error 발생에 의한 프로그램 오작동
  ※ 연결이 종료된 디비에 접근, 손상된 파일에 접근 등

결론

* 프로그램에 문제가 있거나 예외처리가 되지않았을때 '오류가 발생했다.', '예외처리가 안됬다.'라고 표현된다.
* 버그(bug)와 오류(error)를 궂이 나누자면 '프로그래머에 의한 버그', '사용자에 의한 오류'이다.
* 예외는 프로그램 외적인 요인에 의해 발생하는 것이다.

프로그램 개발시에는 예외에 의해 비정상 작동이 일어나지 않도록 예외처리를 해주어야 한다. 그래야만 견고한 프로그램이 되고 사용자는 이를 믿고 사용할 수 있게 된다. 예외처리에는 다음 2가지 유형이 있다.

1. 반환 값을 사용한 예외처리
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
#include "stdafx.h"
#include <iostream>
 
using namespace std;
 
class MyException
{
public:
    MyException(int number)
    {
        this->number = number;
    }
 
    int number;
    // >> 반환값을 사용한 예외처리
    // 더큰수만 입력받는 함수
    bool Test(int bigNumber){
        if (this->number >= bigNumber) return false;
        this->number = bigNumber;
 
        return true;
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    MyException myException(100);
 
    bool isTrue = myException.Test(50);
    if (!isTrue)
        cout << "Error : 터큰수만 입력가능합니다.\n";
 
    return 0;
}
장점
* 간편히 구현이 가능하다.
단점
* 호출시 마다 반환값을 받아 비교해 에러를 알려야한다.
* 프로그램이 커진다면 소스코드에 예외처리 코드가 엉켜 지저분하다.
* 반환 값이 결과를 알리는 목적이기 때문에 다른 용도로는 사용이 불가하다.
2. try catch를 활용한 예외처리
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
#include "stdafx.h"
#include <iostream>
#include <string>
 
using namespace std;
 
class MyException1
{
public:
    MyException1();
    MyException1(int code, string message){
        this->errorCode = code;
        this->errorMessage = message;
    }
    int errorCode;
    string errorMessage;
};
 
class MyException2
{
public:
    MyException2()
    {
        cout << "여러가지 Exception이 가능합니다.\n";
    }
};
 
class MyClass
{
public:
    MyClass(int number)
    {
        this->number = number;
    }
 
    int number;
    void Test(int bigNumber){
        if (this->number >= bigNumber) throw MyException1(100, "Error : 더큰수만 입력가능합니다.\n");
        if (bigNumber > 200) throw MyException2();
        this->number = bigNumber;
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    // 기본적인 예외처리
    try
    {
        MyClass myClass1(100);
        myClass1.Test(50);
    }
    catch (MyException1& ex)
    {
        cout << ex.errorMessage;
    }
 
    // 여러가지 예외상황을 고려할 수 있다.
    try
    {
        MyClass myClass2(100);
        myClass2.Test(201);
    }
    catch (MyException1& ex)
    {
        cout << ex.errorMessage;
    }
    catch (MyException2& ex)
    {
        cout << "Error : 너무 큰수를 입력하였습니다.\n";
    }
 
    return 0;
}
* 예외에 대하여 유연하게 처리할 수 있다.
* 여러예외를 잡아낼 수 있다.
* 예외 객체는 가능하면 참조로 받아 메모리 관리에 불편함이 없도록 하자.(MyException1& ex)
  ※ 객체로 받을 경우 불필요한 메모리 복사발생(MyException1 ex)
  ※ 포인터로 받을 경우 메모리 할당과 해제를 신경써줘야한다.(MyException1* ex)
* 여러 함수를 건너 사용할 수 있다.