2-2. 例外処理

公開日: 17:39 2. 応用編/2-2. 例外処理

例外はプログラムの実行中に生じる問題です。C++における例外とは実行中にゼロ除算を試みるなど、例外的な状況に対する反応です。 例外処理はプログラムのある場所から制御を移す機能を備えています。C++の例外処理はtrycatchthrowの3つのキーワードから成り立っています。

  • try: tryブロックは特定の例外が発生しうるコードを識別します。1つ以上のcatchブロックが後ろに続きます。
  • catch : プログラム内の問題を処理したい場所で例外処理によって例外を捉えます。catchキーワードは例外の捕捉を示します。
  • throw : 問題が現れた時に例外を投げます。これはthrowキーワードによって行われます。

例外が発生し、例外を捉える方法はtrycatchキーワードの組み合わせて例外を補足すると仮定します。try/catchブロックは例外が生まれそうな場所の近くに配置されます。try/catchブロック内のコードは保護されたコードと呼ばれ、try/catchを使った構文は次のようになります。

try
{
   // 保護されたコード
}catch( ExceptionName e1 )
{
   // catchブロック
}catch( ExceptionName e2 )
{
   // catchブロック
}catch( ExceptionName eN )
{
   // catch block
}
tryブロック内で異なる状況で1つ以上の例外が発生する場合には、異なる種類の例外を補足するために、複数のcatch構文を並べていくことが可能です。

例外を投げる

例外はthrow構文を使っているブロック内のどこからでも投げられます。throw構文の被演算子は例外の種類を決定し、任意の式をとり、式の結果の型が投げられた例外の型を決定します。 以下は0除算が発生した時に投げる例外の例です。

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

例外の捕捉

tryブロックに続くcatchブロックはあらゆる例外を補足します。どの例外の型を補足したいか指定でき、これはcatchキーワードの後ろの括弧内に記述される例外宣言によって決められます。

try
{
   // 保護されたコード
}catch( ExceptionName e )
{
  // 例外ExceptionNameを処理するコード
}

上のコードはExceptionName型の例外を補足します。tryブロック内で投げられるあらゆる例外の型をcatchブロックで処理したいのであれば、次のように例外宣言を囲む括弧の間に省略記号(...)を入れる必要があります。

try
{
   // 保護されたコード
}catch(...)
{
  // あらゆる例外を処理するコード
}

以下は0除算例外を投げ、catchブロックで補足する例です。


#include <iostream>
using namespace std;

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "0除算!";
   }
   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*を使わなければいけません。コンパイルし実行すると、次のような結果になります。
0除算!

C++標準例外

C++は<exception>に定義された標準例外を備えており、プログラムで利用することができます。これらは次の親子クラス階層に配置されています。



C++例外階層

上で述べた各例外の簡単な説明です。

 
例外説明
std::exception例外であり、すべてのC++標準例外の親クラス
std::bad_allocnewによって投げられる。
std::bad_castdynamic_castによって投げられる。
std::bad_exceptionC++プログラム内の予期せぬ例外を処理するデバイス。
std::bad_typeidtypeidによって投げられる。
std::logic_errorコード読み取りで検出される理論上の例外。
std::domain_error数学的に無効な領域が使われた時に投げられる例外。
std::invalid_argument無効な引数が原因で投げられる例外。
std::length_error大きすぎるstd::stringが生成された時に投げられる例外。
std::out_of_rangestd::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()はexceptionクラスが備えているpublicなメソッドで、子exceptionクラスにオーバーライドされます。これは例外の原因を返します。
  • ?±??G???g???[?d????u?b?N?}?[?N???A

0 件のコメント :

コメントを投稿