C++ 프로그래밍

C++의 예외 처리: try, throw, catch

ROBL 2025. 2. 10.
728x90
반응형

C++에서 예외 처리는 프로그램의 오류를 관리하고, 프로그램의 안정성을 높이는 중요한 기법입니다. 예외 처리를 통해 프로그램이 예기치 않은 상황에서도 정상적으로 작동할 수 있도록 할 수 있습니다. 이번 포스팅에서는 C++의 예외 처리 메커니즘인 try, throw, catch에 대해 자세히 살펴보겠습니다.

1. 예외 처리의 필요성

프로그램 실행 중 발생할 수 있는 오류는 여러 가지가 있습니다. 예를 들어, 파일을 열 수 없거나, 메모리 할당에 실패하거나, 잘못된 입력이 들어오는 경우 등이 있습니다. 이러한 오류를 적절히 처리하지 않으면 프로그램이 비정상적으로 종료되거나, 잘못된 결과를 초래할 수 있습니다. 예외 처리는 이러한 오류를 관리하는 방법을 제공합니다.

예외 처리를 사용하지 않으면, 오류가 발생했을 때 프로그램이 예기치 않게 종료되거나, 오류 메시지를 출력하지 않고 잘못된 결과를 반환할 수 있습니다. 예외 처리를 통해 이러한 문제를 예방하고, 프로그램의 안정성을 높일 수 있습니다.

2. C++의 예외 처리 구조

C++에서 예외 처리는 try, throw, catch 키워드를 사용하여 구현됩니다.

2.1 try 블록

try 블록은 예외가 발생할 가능성이 있는 코드를 포함합니다. 이 블록 내에서 예외가 발생하면, 프로그램의 흐름은 즉시 catch 블록으로 이동합니다. try 블록은 반드시 catch 블록과 함께 사용해야 하며, 예외가 발생하지 않으면 catch 블록은 실행되지 않습니다.

try {
    // 예외가 발생할 수 있는 코드
}

2.2 throw 문

throw 문은 예외를 발생시키는 데 사용됩니다. 예외가 발생하면, throw 문 뒤에 오는 값이나 객체가 예외로 전달됩니다. throw 문은 함수 내에서 호출할 수 있으며, 예외를 발생시킨 후에는 해당 함수의 실행이 중단되고, 호출한 쪽으로 제어가 넘어갑니다.

throw exception_object; // 예외 객체를 던짐

2.3 catch 블록

catch 블록은 try 블록에서 발생한 예외를 처리하는 코드입니다. catch 블록은 특정 타입의 예외를 처리할 수 있으며, 여러 개의 catch 블록을 사용하여 다양한 예외를 처리할 수 있습니다. catch 블록은 예외 객체를 매개변수로 받아, 예외에 대한 정보를 얻을 수 있습니다.

catch (exception_type e) {
    // 예외 처리 코드
}

3. 예외 처리 예제

다음은 C++에서 예외 처리를 사용하는 간단한 예제입니다.

#include <iostream>
#include <stdexcept> // std::runtime_error 사용을 위한 헤더
using namespace std;

void divide(int a, int b) {
    if (b == 0) {
        throw runtime_error("Division by zero!"); // 예외 발생
    }
    cout << "Result: " << a / b << endl;
}

int main() {
    try {
        divide(10, 0); // 0으로 나누기 시도
    } catch (const runtime_error& e) {
        cout << "Caught an exception: " << e.what() << endl; // 예외 처리
    }

    return 0;
}

예제 설명

  1. divide 함수: 두 개의 정수를 받아 나누기를 수행합니다. 만약 두 번째 인자가 0이라면, throw 문을 사용하여 runtime_error 예외를 발생시킵니다.
  2. main 함수: try 블록 내에서 divide 함수를 호출합니다. 이때 0으로 나누기를 시도하므로 예외가 발생합니다.
  3. catch 블록: 발생한 예외는 catch 블록에서 처리되며, 예외 메시지를 출력합니다. e.what() 메서드를 사용하여 예외의 설명을 가져올 수 있습니다.

4. goto문과 if-else문과의 비교

try-catch문을 goto문, if-else문과 비교할 수 있습니다. 아래는 각각의 방법을 사용한 예제입니다.

4.1 goto문 예제

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 0;
    int result;

    if (b == 0) {
        goto error; // 오류 발생 시 error 레이블로 이동
    }

    result = a / b;
    cout << "Result: " << result << endl;
    return 0;

error:
    cout << "Error: Division by zero!" << endl; // 오류 처리
    return 1;
}

설명

  • 위의 예제에서 goto문을 사용하여 오류가 발생했을 때 error 레이블로 이동합니다.
  • 이 방법은 코드의 흐름을 비직관적으로 만들고, 가독성을 떨어뜨립니다.
  • 오류 처리 로직이 코드의 주요 흐름과 섞여 있어, 프로그램의 흐름을 이해하기 어렵게 만듭니다.

4.2 if-else문 예제

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 0;
    int result;

    if (b == 0) {
        cout << "Error: Division by zero!" << endl; // 오류 처리
    } else {
        result = a / b;
        cout << "Result: " << result << endl;
    }

    return 0;
}

설명

  • 위의 예제에서는 if-else문을 사용하여 오류를 처리합니다.
  • 오류가 발생하면 오류 메시지를 출력하고, 그렇지 않으면 나누기 결과를 출력합니다.
  • 그러나 이 경우에도 오류 처리 로직이 코드의 주요 흐름과 섞여 있어 가독성이 떨어질 수 있습니다.

4.3 try-catch문 예제

#include <iostream>
#include <stdexcept> // std::runtime_error 사용을 위한 헤더
using namespace std;

void divide(int a, int b) {
    if (b == 0) {
        throw runtime_error("Division by zero!"); // 예외 발생
    }
    cout << "Result: " << a / b << endl;
}

int main() {
    try {
        divide(10, 0); // 0으로 나누기 시도
    } catch (const runtime_error& e) {
        cout << "Caught an exception: " << e.what() << endl; // 예외 처리
    }

    return 0;
}

설명

  • try 블록 내에서 오류가 발생하면 즉시 catch 블록으로 이동하여 오류를 처리합니다.
  • 이 방법은 오류 처리 로직을 명확하게 분리하여 코드의 가독성을 높이고, 프로그램의 흐름을 이해하기 쉽게 만듭니다.
  • 또한, 여러 개의 예외를 처리할 수 있는 유연성을 제공합니다.

5. 예외 처리의 장점

5.1 안정성

예외 처리를 통해 프로그램이 예기치 않은 상황에서도 안정적으로 작동할 수 있습니다. 예외가 발생하더라도 프로그램이 비정상적으로 종료되지 않고, 적절한 오류 메시지를 출력하거나 대체 동작을 수행할 수 있습니다.

5.2 코드 가독성

예외 처리를 사용하면 오류 처리 코드를 명확하게 분리할 수 있어 코드의 가독성이 향상됩니다. 예외 처리 로직이 trycatch 블록으로 구분되어 있어, 코드의 흐름을 쉽게 이해할 수 있습니다.

5.3 유지보수 용이성

예외 처리를 통해 오류 처리 로직을 중앙 집중화할 수 있어 유지보수가 용이합니다. 새로운 예외 유형이 추가되거나 기존 예외 처리 로직이 변경될 때, 해당 부분만 수정하면 되므로 코드의 일관성을 유지할 수 있습니다.

6. 예외 처리의 활용

예외 처리는 다양한 상황에서 활용될 수 있습니다. 다음은 몇 가지 예시입니다.

6.1 파일 입출력

파일을 열거나 읽는 과정에서 발생할 수 있는 오류를 처리할 때 예외 처리를 사용할 수 있습니다. 예를 들어, 파일이 존재하지 않거나 읽기 권한이 없는 경우 예외를 발생시켜 적절한 오류 메시지를 출력할 수 있습니다.

6.2 메모리 관리

동적 메모리 할당 시 메모리 부족으로 인한 오류를 처리할 때 예외 처리를 사용할 수 있습니다. new 연산자를 사용할 때 메모리 할당에 실패하면 std::bad_alloc 예외가 발생하므로, 이를 catch 블록에서 처리할 수 있습니다.

6.3 사용자 입력 검증

사용자로부터 입력을 받을 때, 잘못된 형식의 입력이나 범위를 벗어난 입력에 대해 예외를 발생시켜 처리할 수 있습니다. 이를 통해 프로그램이 잘못된 입력으로 인해 비정상적으로 종료되는 것을 방지할 수 있습니다.

7. catch 블록의 다중화

C++에서는 여러 개의 catch 블록을 사용하여 다양한 예외를 처리할 수 있습니다. 각 catch 블록은 특정 타입의 예외를 처리하도록 정의할 수 있으며, 예외가 발생하면 해당 타입에 맞는 catch 블록이 실행됩니다.

try {
    // 예외가 발생할 수 있는 코드
} catch (const std::runtime_error& e) {
    // runtime_error 예외 처리
} catch (const std::out_of_range& e) {
    // out_of_range 예외 처리
} catch (...) {
    // 모든 예외 처리
}

위의 예제에서 catch 블록은 runtime_errorout_of_range 예외를 각각 처리하며, 마지막 catch(...) 블록은 모든 예외를 처리하는 역할을 합니다. 이를 통해 다양한 예외 상황에 대해 유연하게 대응할 수 있습니다.

8. 사용자 정의 예외 클래스

C++에서는 사용자 정의 예외 클래스를 만들어 특정한 예외 상황을 처리할 수 있습니다. 사용자 정의 예외 클래스는 std::exception 클래스를 상속받아 구현할 수 있습니다.

#include <iostream>
#include <exception>
using namespace std;

class MyException : public exception {
public:
    const char* what() const noexcept override {
        return "My custom exception occurred!";
    }
};

void test() {
    throw MyException(); // 사용자 정의 예외 발생
}

int main() {
    try {
        test();
    } catch (const MyException& e) {
        cout << "Caught an exception: " << e.what() << endl; // 사용자 정의 예외 처리
    }

    return 0;
}

위의 예제에서 MyException 클래스는 std::exception을 상속받아 사용자 정의 예외를 구현합니다. what() 메서드를 오버라이드하여 예외 메시지를 반환합니다. test 함수에서 이 예외를 발생시키고, main 함수에서 이를 처리합니다.

9. 결론

C++의 예외 처리 메커니즘인 try, throw, catch를 사용하면 프로그램의 오류를 효과적으로 관리할 수 있습니다. 이를 통해 프로그램의 안정성을 높이고, 가독성과 유지보수성을 향상시킬 수 있습니다. 예외 처리는 모든 C++ 프로그래머가 숙지해야 할 중요한 개념입니다. 다음 포스팅에서는 람다식과 함수 객체를 알아보겠습니다.

728x90
반응형

댓글

💲 추천 글