본문 바로가기
프로그래밍/C++

C++ ] 예외처리 try-catch

by eteo 2023. 5. 28.

 

 

 

C++에서 try-catch 문은 예외가 발생할 수 있는 코드 블록을 감싸고, 발생한 예외를 적절하게 처리하는 메커니즘을 제공한다.

 

try-catch 기본 구문

 

try {
    // 예외가 발생할 수 있는 코드
} catch (예외_유형1& e) {
    // 예외_유형1에 해당하는 예외 처리
} catch (예외_유형2& e) {
    // 예외_유형2에 해당하는 예외 처리
} catch (...) {
    // 기타 모든 예외를 처리하는 블록
}

 

  • try 블록: 예외가 발생할 수 있는 코드를 포함하는 블록. 이 블록 내에서 예외가 발생하면 예외를 던진다.
  • catch 블록: 발생한 예외를 처리하기 위한 블록. catch 블록은 발생한 예외의 유형에 따라 여러 개를 사용할 수 있다. 가장 specific한 예외 유형부터 순서대로 체크하며, 마지막 catch 블록은 모든 예외를 처리하기 위해 사용된다. 이 블록은 예외의 유형을 명시하지 않고, ...으로 표시할 수 있다.
  • 예외 유형: 처리할 예외의 유형을 나타내는 C++ 예외 클래스. 예외 유형은 사용자 정의 예외 클래스일 수도 있고, 표준 예외 클래스 중 하나일 수도 있다.
  • e: 예외 객체의 이름. 예외 객체에는 발생한 예외에 대한 정보가 포함되어 있으며, catch 블록 내에서 참조할 수 있다.

 

 

 

 

 

사용자가 직접 std::runtime_error 예외를 던지고 해당 예외 객체에 문자열 메시지를 전달하는 예제

 

#include <iostream>

int main() {
    try {
        int dividend, divisor;
        
        std::cout << "Enter dividend: ";
        std::cin >> dividend;
        
        std::cout << "Enter divisor: ";
        std::cin >> divisor;
        
        if (divisor == 0) {
        // std::runtime_error 예외 던지면서 메시지 전달
            throw std::runtime_error("Division by zero is not allowed.");
        }
        
        double result = static_cast<double>(dividend) / divisor;
        std::cout << "Result: " << result << std::endl;
    } catch (const std::exception& e) {
    // 예외 객체를 통해 메시지 출력
        std::cout << "An exception occurred: " << e.what() << std::endl;
    }
    
    return 0;
}

 

 

✔ C++ 표준 라이브러리의 std::exception 클래스를 상속받는 여러 예외 클래스

 

  • std::runtime_error : 프로그램 실행 중 발생하는 오류
  • std::logic_error : 논리 오류. 예를 들어, 잘못된 인자를 전달한 경우 등
  • std::invalid_argument : 잘못된 인수를 전달한 경우 발생하는 예외 클래스
  • std::out_of_range : 범위를 벗어난 인덱스나 값에 접근한 경우 발생하는 예외 클래스
  • std::bad_alloc : 동적 메모리 할당에 실패한 경우 발생하는 예외 클래스

이 외에도 다양한 예외 클래스가 있다.

사용자 정의 예외 객체를 만들어 예외를 throw할 수도 있고, 명시적으로 throw 문을 사용하지 않아도 일부 상황에서 자동으로 예외를 발생시키고 처리할 수 있는 예외 처리 메커니즘을 제공된다

 

 

 

 

✔ std::exception은 C++ 표준 예외 클래스의 기본 클래스로 이 매개변수 유형을 사용하면 모든 종류의 예외를 잡을 수 있다. 하지만, catch 블록에서 std::exception을 사용하여 모든 예외를 처리하는 것은 예외 처리의 마지막 수단으로 사용되어야 한다.

왜냐하면 예외의 구체적인 유형을 알 수 없기 때문에, 다운캐스팅(dynamic_cast)의 방법을 쓰지 않는 이상 예외의 세부 정보를 알 수 없고 적절한 조치를 취하는 것이 어렵기 때문이다. 때문에 최대한 예외 유형에 따라 명시적인 catch 블록을 먼저 사용하여 처리하는 것이 좋다.

 

 

예를들어 보자.

std::filesystem::filesystem_error는 <filesystem> 헤더에서 정의된 특정 예외 클래스로 파일 시스템 작업의 실패 원인에 대한 정보를 보다 자세히 제공한다. 이렇게 보다 구체적인 예외 클래스를 사용하면 예외를 더 쉽게 처리하고, 예외 객체에서 세부 정보에 접근할 수 있다.

#include <iostream>
#include <filesystem>

int main() {
    std::filesystem::path sourceFile("path/to/source.txt");
    std::filesystem::path destinationFile("path/to/destination.txt");
    
    try {
        std::filesystem::rename(sourceFile, destinationFile);
        std::cout << "File moved successfully." << std::endl;
    } catch (const std::filesystem::filesystem_error& e) {
        std::cout << "Failed to move file: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "exception : " << e.what() << std::endl;
    }
    
    return 0;
}