C++ 예외 처리 try catch throw

C 언어와 다르게, C++은 애러를 핸들링할 수 있는 try-catch문을 제공합니다.

비교조건문 if예외 처리 try-catch
핸들링return 값의 구분으로 핸들링throw 를 사용하여 catch로 분기 및 예외 처리
단점return 자료형에 종속적반드시 try-catch 블럭에서 처리

조건문은 반환 값의 구분으로 핸들링하기 때문에 반환하는 자료형에 종속적입니다. 예를 들어 char getSome()이라는 함수에서 예외 처리를 하고 싶은데 char형으로 어떻게 구분을 지을 수 있을까요? NULL값을 줄 수도 있지만 정말 NULL을 반환한게 정상적인 흐름이라면 오류가 아닐텐데 말이죠!

⚠️ Warning

NULL 값을 반환하는게 정상일리는 없죠. 의도만 파악해주세요.

 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
#include <iostream>
#include <cstdlib>
#include <ctime>

char getSome()
{
    srand(time(NULL));
    char result;
    char randomValue = rand() % 1000;
    if(randomValue % 2 == 0)
    {
        // 정상
        result = randomValue;
    }
    else
    {
        // 애러
        result = '\0';
        /*NULL을 반환함으로서 애러를 표현했지만
        정상적인 경우에도 NULL이 나올 수 있음*/
    }

    return result;
}

int main(int argc, char **argv)
{
    char value = getSome();

    // NULL값도 정상인데? 처리할 방법이 없네!
    if(value != '\0')
        std::cout << value << std::endl;

    return 0;
}

Usage

위 극단적인 예시를 갖고 try-catch를 사용하여 예외 처리를 해보겠습니다.

Example 📝

 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
#include <iostream>
#include <cstdlib>
#include <ctime>

char getSome()
{
    srand(time(NULL));
    char result;
    char randomValue = rand() % 1000;
    if(randomValue % 2 == 0)
    {
        // 정상
        result = randomValue;
    }
    else
    {
        // 애러
        throw -1;
    }

    return result;
}

int main(int argc, char **argv)
{
    try
    {
        char value = getSome();

        std::cout << value << std::endl;
        
    } catch(int e)
    {
        std::cout << "error:" << e << std::endl;
    }

    return 0;
}
  • 18: throw를 이용하여 정상이 아닐 경우에 catch-1의 값을 던집니다.
  • 32: -1을 예외로 전달했기 때문에 int 자료형으로 인자를 받습니다.
  • 34: 예외 처리로 애러 코드를 console에 전달합니다.

또한 함수의 return으로 구분하지 않아 자료형 선택이 자유롭고 더 나아가 특정 예외 처리를 위한 자료형도 만들 수 있습니다.

예외 처리를 위한 자료형은 어떻게 만들 수 있을까요? 애러 코드 및 메세지가 정의되어야 할 것이고, 간단히 콘솔 또는 파일로 출력해주는 함수가 있으면 좋을 것 같습니다.

코드와 메세지를 정의할 수 있는 변수와 출력을 하는 함수가 필요하므로 클래스로 만들어 볼 수 있습니다. CharException 이라는 클래스를 만들고 정의를 해보도록 하겠습니다.

 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
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <cstdlib>
#include <ctime>

enum Error
{
    NOT_MULTIPLE_TWO = -1
};

class CharException
{
private:

    Error error;

public:

    explicit CharException(Error e)
    {
        error = e;
    }

    void putLogToConsole()
    {
        std::string msg;

        switch(error)
        {
            case(NOT_MULTIPLE_TWO): 
                msg.assign("is not multiple of two"); 
                break;

            default: 
                msg.assign("Unknown error"); 
                break; 
        }

        std::cout << msg << std::endl;
    }
};

char getSome()
{
    srand(time(NULL));
    char result;
    char randomValue = rand() % 1000;
    if(randomValue % 2 == 0)
    {
        // 정상
        result = randomValue;
    }
    else
    {
        // 애러
        throw CharException(NOT_MULTIPLE_TWO);
    }

    return result;
}

int main(int argc, char **argv)
{
    try
    {
        char value = getSome();

        std::cout << value << std::endl;
        
    } catch(CharException e)
    {
        e.putLogToConsole();
    }

    return 0;
}
  • 5: enum 키워드를 사용하여 애러 코드를 상수로 사용했습니다.
  • 10: CharException 클래스, error 멤버 변수와 콘솔에 해당 애러 코드에 관한 메세지를 출력하기 위한 함수를 만들었습니다.
  • 55: throw에 기존 -1 대신 특별 예외 처리를 위한 자료형 CharException 클래스를 사용하여 catch로 던집니다.
  • 69: 예외 처리에서는 콘솔에 메세지를 출력하는 함수를 이용하여 애러 메세지를 출력합니다.

⚠️ Warning

위 예시처럼 예외 처리 부분에 상세한 로그 내용(like stack trace)을 사용자에게 출력하는 행위는 보안상 안전하지 않습니다.

반드시 예외를 처리할 수 있는 로직이 올 수 있도록 작성해 주세요!

💡 Tips

이미 표준으로 정의되어 있는 Exception이 있습니다.

상세한 정보를 보시려면 여기를 참고하세요!

되도록 표준을 많이 사용하고 없는 경우 만들어 사용하면 될 것 같습니다.

성능 🚀

try-catchif를 단순 비교했을 때 try-catch가 더 많은 일을 수행하기 때문에 약간 뒤처진다고 합니다. 예외 처리가 되지 않을때는 if와 성능차이 없음 따라서 실시간 처리를 요구하거나 높은 성능을 요구하는 시스템에서 부적절할 수 있겠으나, 개발자가 예상할 수 있는 에러를 핸들링할 수 있다는 점이 크기 때문에 사용하는 것 같습니다.

💡 자세한 내용은 이 곳에서 확인해주세요!