본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::exception (98, 11, 17)

by snowoods 2025. 10. 16.

Modern C++

 

std::exception

 

개요

std::exception은 C++ 표준 라이브러리에서 모든 표준 예외 클래스의 기반이 되는 클래스입니다. 프로그램 실행 중 발생하는 오류(예: 잘못된 인수, 0으로 나누기 등)를 처리하기 위한 표준적인 방법을 제공합니다. 사용자는 std::exception을 상속하여 자신만의 예외 클래스를 만들 수 있으며, 이를 통해 오류 처리 코드를 더 명확하고 구조적으로 작성할 수 있습니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98: std::exception이 표준 라이브러리에 처음 도입되었습니다.
  • C++11: noexcept 키워드가 도입되어, 함수가 예외를 발생시키지 않음을 명시할 수 있게 되었습니다. what() 메소드 오버라이드 시 noexcept를 사용하는 것이 권장됩니다.
  • C++17: std::string_view가 도입되어, 문자열을 소유권 없이 효율적으로 참조할 수 있게 되었습니다. 예제 코드에서 메시지를 전달하는 데 사용되었습니다.

 

내용 설명

std::exception 클래스는 가상(virtual) 소멸자와 what()이라는 가상 멤버 함수 하나를 가집니다.

  • virtual const char* what() const noexcept;

what() 함수는 예외의 원인을 설명하는 C-스타일 문자열(const char*)을 반환합니다. 이 함수는 예외를 던지지 않음을 보장하기 위해 noexcept로 선언되어 있습니다.

사용자 정의 예외 클래스를 만들 때는 std::exception을 공개(public) 상속하고, what() 함수를 재정의(override)하여 해당 예외에 대한 구체적인 설명 메시지를 제공하는 것이 일반적입니다.

제공된 예제에서는 DivisionByZero라는 사용자 정의 예외 클래스를 정의했습니다. 이 클래스는 std::exception을 상속하고 what() 함수를 재정의하여 "DivisionByZero 1"이라는 메시지를 반환합니다. div 함수 내에서 0으로 나누려는 시도가 있을 때 throw 키워드를 사용해 이 예외를 발생시킵니다. main 함수의 try-catch 블록은 이 예외를 잡아 처리합니다.

 

예제 코드

#include <exception>
#include <iostream>
#include <string>
#include <string_view>

class DivisionByZero : public std::exception
{
public:
    DivisionByZero(std::string_view _message) : message(_message){};

    std::string message;

    [[nodiscard]] const char *what() const noexcept override
    {
        return message.c_str();
    }
};

double div(double x, double y)
{
    if (y == 0.0)
    {
        throw DivisionByZero("DivisionByZero 1");
        // throw std::invalid_argument("DivisionByZero 2");
    }

    return x / y;
}

int main()
{
    try
    {
        div(1.0, 0.0);
    }
    catch (const std::exception &ex)
    {
        std::cerr << ex.what() << '\n';
    }

    return 0;
}

 

실행 결과

DivisionByZero 1

 

활용팁

  • 상수 참조로 예외 잡기: catch (const std::exception& ex)와 같이 예외를 상수 참조(const reference)로 잡는 것이 좋습니다. 이는 불필요한 객체 복사를 방지하고, 다형성을 활용하여 자식 클래스의 예외도 부모 클래스 타입으로 받을 수 있게 합니다. (객체 잘림(slicing) 문제 방지)
  • 구체적인 예외 클래스 사용: std::runtime_error, std::invalid_argument 등 표준 라이브러리에서 제공하는 구체적인 예외 타입을 사용하거나, 직접 의미 있는 이름의 예외 클래스를 만들어 사용하면 코드의 가독성과 유지보수성이 향상됩니다.
  • noexcept 준수: what() 함수를 재정의할 때는 반드시 noexcept를 명시해야 합니다. 예외 처리 중에 또 다른 예외가 발생하면 프로그램이 std::terminate를 호출하며 즉시 종료될 수 있기 때문입니다.