C++ 예외 처리


예외는 프로그램 실행 중에 발생하는 문제입니다. C++ 예외는 0으로 나누려는 시도와 같이 프로그램이 실행되는 동안 발생하는 특별한 상황입니다.

예외는 프로그램 제어를 전송하는 방법을 제공합니다. C++ 예외 처리에는 try, catch 및 throw라는 세 가지 키워드가 포함됩니다.

  • throw: 문제가 발생하면 프로그램에서 예외가 발생합니다. 이는 throw 키워드를 사용하여 수행됩니다.

  • catch: 문제를 처리하려는 지점에서 예외 처리기를 사용하여 예외를 포착합니다. catch 키워드는 예외를 잡는 데 사용됩니다.

  • try: try 블록의 코드는 활성화될 특정 예외를 식별합니다. 일반적으로 다음에는 하나 이상의 catch 블록이 옵니다.

블록에서 예외가 발생하는 경우 예외를 포착하는 방법은 trycatch 키워드를 사용합니다. 예외를 발생시킬 수 있는 코드는 try 블록에 배치됩니다. try 블록에 있는 코드를 보호 코드라고 합니다. try/catch 문을 사용하는 구문은 다음과 같습니다.

try
{
   // 保护代码
}catch( ExceptionName e1 )
{
   // catch 块
}catch( ExceptionName e2 )
{
   // catch 块
}catch( ExceptionName eN )
{
   // catch 块
}

try 블록이 다양한 상황에서 다양한 예외를 발생시키는 경우 여러 catch 문을 나열하여 다양한 유형의 예외를 포착할 수 있습니다.

예외 발생

throw 문을 사용하여 코드 블록의 어느 위치에서나 예외를 발생시킬 수 있습니다. throw 문의 피연산자는 모든 표현식이 될 수 있으며 표현식 결과 유형에 따라 발생하는 예외 유형이 결정됩니다.

다음은 0으로 나누려고 할 때 발생하는 예외의 예입니다.

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}

catch 예외

catch 블록은 try 블록을 따라가며 예외를 포착합니다. catch 키워드 뒤의 괄호 안에 있는 예외 선언에 따라 결정되는 catch하려는 예외 유형을 지정할 수 있습니다.

try
{
   // 保护代码
}catch( ExceptionName e )
{
  // 处理 ExceptionName 异常的代码
}

위 코드는 ExceptionName 유형의 예외를 포착합니다. catch 블록이 try 블록에서 발생한 모든 유형의 예외를 처리할 수 있도록 하려면 다음과 같이 예외 선언의 대괄호 안에 줄임표를 사용해야 합니다.

try
{
   // 保护代码
}catch(...)
{
  // 能处理任何异常的代码
}

다음은 나누기를 던지는 예입니다. 제로 예외로 처리하고 catch 블록에서 예외를 포착합니다.

#include <iostream>
using namespace std;

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}

int main ()
{
   int x = 50;
   int y = 0;
   double z = 0;
 
   try {
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg) {
     cerr << msg << endl;
   }

   return 0;
}

const char* 유형의 예외를 발생시키므로 예외를 잡을 때 catch 블록에서 const char*를 사용해야 합니다. 위 코드를 컴파일하고 실행하면 다음과 같은 결과가 생성됩니다.

Division by zero condition!

C++ 표준 예외

C++는 <예외>에 정의된 일련의 표준 예외를 제공하며, 이를 프로그램에서 사용할 수 있습니다. 이 기준에. 이는 다음과 같이 상위-하위 클래스 계층 구조로 구성됩니다.

cpp_exceptions.jpg

다음 표는 위 계층 구조에서 발생하는 각 예외에 대한 설명입니다.

ExceptionDescription
std::Exception이 예외는 모든 표준 C++ 예외의 상위 클래스입니다.
std::bad_alloc이 예외는 new에 의해 발생할 수 있습니다.
std::bad_cast이 예외는 dynamic_cast에 의해 발생할 수 있습니다.
std::bad_Exception이는 C++ 프로그램에서 예상치 못한 예외를 처리할 때 매우 유용합니다.
std::bad_typeid이 예외는 typeid에 의해 발생할 수 있습니다.
std::logic_error이론적으로 코드를 읽어 감지할 수 있는 예외입니다.
std::domain_error이 예외는 잘못된 수학 도메인이 사용될 때 발생합니다.
std::invalid_argument이 예외는 잘못된 매개변수가 사용되는 경우 발생합니다.
std::length_error이 예외는 너무 긴 std::string이 생성될 때 발생합니다.
std::out_of_range이 예외는 std::Vector 및 std::bitset<>::operator[]()와 같은 메서드에 의해 발생할 수 있습니다.
std::runtime_error이론적으로 코드를 읽어도 감지할 수 없는 예외입니다.
std::overflow_error이 예외는 수학적 오버플로가 발생할 때 발생합니다.
std::range_error이 예외는 범위를 벗어난 값을 저장하려고 할 때 발생합니다.
std::underflow_error이 예외는 수학적 언더플로가 발생할 때 발생합니다.

새 예외 정의

Exception 클래스를 상속하고 오버로드하여 새 예외를 정의할 수 있습니다. 다음 예제에서는 std::Exception 클래스를 사용하여 사용자 고유의 예외를 구현하는 방법을 보여줍니다.

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

struct MyException : public exception
{
  const char * what () const throw ()
  {
    return "C++ Exception";
  }
};
 
int main()
{
  try
  {
    throw MyException();
  }
  catch(MyException& e)
  {
    std::cout << "MyException caught" << std::endl;
    std::cout << e.what() << std::endl;
  }
  catch(std::exception& e)
  {
    //其他的错误
  }
}

이렇게 하면 다음과 같은 결과가 생성됩니다.

MyException caught
C++ Exception

여기서 what()은 예외 클래스에서 제공하는 공용 메서드입니다. 모든 하위 예외 클래스에 대해 오버로드되었습니다. 그러면 예외 이유가 반환됩니다.