C++ 例外処理


例外とは、プログラムの実行中に発生する問題です。 C++ 例外は、ゼロによる除算の試みなど、プログラムの実行中に発生する特殊な状況です。

例外は、プログラム制御を転送する方法を提供します。 C++ 例外処理には、try、catch、throw という 3 つのキーワードが含まれます。

  • throw: 問題が発生すると、プログラムは例外をスローします。これは、throw キーワードを使用して行われます。

  • catch: 問題を処理したい箇所で例外ハンドラーを使用して例外をキャッチします。 catch キーワードは例外をキャッチするために使用されます。

  • try: try ブロック内のコードは、アクティブ化される特定の例外を識別します。通常、その後に 1 つ以上の catch ブロックが続きます。

ブロックが例外をスローした場合、例外をキャッチするメソッドは try キーワードと catch キーワードを使用します。例外をスローする可能性のあるコードは try ブロックに配置されます。try ブロック内のコードは保護コードと呼ばれます。 try/catch ステートメントを使用するための構文は次のとおりです。

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

try ブロックがさまざまな状況で異なる例外をスローする場合は、複数の catch ステートメントをリストしてさまざまなタイプの例外をキャッチすることを試みることができます。

例外をスローする

throw ステートメントを使用すると、コード ブロック内のどこにでも例外をスローできます。 throw ステートメントのオペランドには任意の式を指定でき、式の結果の型によってスローされる例外の型が決まります。

以下は、ゼロ除算しようとしたときにスローされる例外の例です:

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

catch例外

catchブロックは、tryブロックの後に続き、例外をキャッチします。キャッチする例外のタイプを指定できます。これは、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この例外は、数学的アンダーフローが発生した場合にスローされます。

新しい例外を定義する

例外クラスを継承してオーバーロードすることで、新しい例外を定義できます。次の例は、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() は例外クラスによって提供されるパブリック メソッドです。すべてのサブ例外クラスに対してオーバーロードされています。これにより、例外の理由が返されます。