C++/例外処理

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++
ナビゲーションに移動 検索に移動
注意
ここで説明されている例外処理の仕様は、C++11でnoexcept機能が搭載されたことにより非推奨となった動的例外仕様(dynamic exception specification)です。動的例外仕様はC++-17で廃止されました[1][2]

例外処理[編集]

例外処理(Exception handling)とは、プログラムに異常が発生したときに、現在の処理を中断してエラーメッセージを表示する処理のことです。

例外処理とは[編集]

C++の例外処理のコードは、次のようになる。

try {
  例外を捕捉する必要のある処理
}
catch( e){
   例外発生時の処理;
}
もし try に続くブロックで例外が発生sた場合 catch で続くブロックの処理が実行されます。
try {
   throw 2;
}
catch(const int i){
   cout << "エラーが発生しました。値は" << e << "です。" << endl;
}
もしtryのブロックの外にthrowを書いた場合、ユーザープログラムによっては捕捉されないので、ランタイムによって捕捉されthrowされた値をランタイムが知らなければ、プログラムは異常終了します。

throwによって、強制的にcatchに制御が移動するため、throw後に書かれたコードは実行されない。

try {
  例外の起こりそうな部分
  if (条件) {
   throw 2;
   cout << "文字列"; //ここは実行されない。
  }
}
上記のコードにある「文字列」は、プログラム実行時には表示されない。

throwによって、catchにエラーが送出される。tryからcatchへのエラーの送出は、変数データや文字列データなど1つのオブジェクトを値として渡す。上記のコードの例の場合では、数値「2」がtryからcatchへと渡される。 tryから渡されるデータの型と、catch()の丸カッコ内に記述されたデータの型が一致した場合、そのcatch文に記述された処理内容が実行される。 上記のコードの場合なら、tryから数値「2」が渡されたので、catch()の丸カッコ内には、int型が書かれている。

if文はなくてもよい[編集]

tryブロック内にif文がなくても、throwさえあれば、catchブロックにエラーを送出できる。 if文のない場合、次のような文例になる。

try{
   throw 2;
}
catch(const int i){
   cout << "エラーが発生しました。";
}
tryブロックのthrow文以降は実行されない
#include <iostream>
using namespace std;

int main() {
  try {
    cout << "まだ投げてない状態。" << endl;
    throw 2;
    cout << "ここは実行されない。" << endl;
  }

  catch (const int i) {
    cout << "エラーが発生しました。" << endl;
  }
}
実行結果
まだ投げてない状態。
エラーが発生しました。

実例[編集]

if文をもちいた文例[編集]

なお、さらにif文を実際の例外処理では使用するので、if文とtry,throw,catchの例外処理は、次のように組み合わされる事が多いだろう。

#include <iostream>
using namespace std;

int main() {
  try {
    if (1) {
      throw 2;
    }
  }
  catch (const int i) {
    cout << "エラーが発生しました。" << endl;
  }
}

なお、上記のコードでは、エラー時の挙動を確認するために、if文の条件を1にすることで、わざとエラーを起こしている。


C++言語のtryはエラー判断を行わない[編集]

C++言語では、tryキーワードは、エラー判断を行わない。(他のプログラミング言語では、try構文がエラー判断を行うプログラミング言語もある。しかしC++言語では、tryキーワードはエラー判断を行わない。)

そのためC++言語で、次のような割り算のプログラムで、0で割り算をすることになるようなエラーになる数値を代入しても、 コード中にthrowがないので、catchブロック内のコードは、なにも実行されない。

#include <iostream>
using namespace std;

int main() {
  cout << "まだ投げてない状態。" << endl;
  try {
    double a, b;
    cout << "数字bを入力してください。3をその数字で割り算します。" << endl;
    cin >> b;
    a = 3.0 / b;
    cout << "3÷b = " << a << endl;
    cout << "計算が終わりました。" << endl;
  }

  catch (const int i) {
    cout << "エラーが発生しました。" << endl;
    exit(EXIT_FAILURE);
  }
}

読者は、上記のコードを実行してみて、数値入力を求められるので、0を入力してみよう。すると、catchブロック内にある「エラーが発生しました。」は表示されない。いっぽう、tryブロック内にある「計算が終わりました。」は表示され、計算が終わるとともにプログラムも終わる。


まとめ[編集]

今までの話をまとめると、例外処理の構文は、次のようになる。

   try{
     例外の起こりそうな部分;
   }
   catch( 変数名){
     例外発生時の処理;
   }

複数のcatch文のあるときの例外処理[編集]

catch文が複数あるとき、投げられた例外は、上の行から順にcatchで受け取れるかどうかが判定され、さいしょに型の一致したcatch文で受け取られたときに、その例外は消失する。そして、その例外は、もし、次の行のcatchでも型が一致しても、もう例外が消失してるので受け取らられない。

なので、たとえば、次のようにcatch文が複数あるとき、実行してみると・・・

#include <iostream>
using namespace std;

int main() {
  try {
    throw 10;
    cout << "ここは実行されない。" << endl;
  }

  catch (const int i) {
    cout << "エラー1が発生しました。" << i << endl;
  } catch (int i) {
    cout << "エラー2が発生しました。" << i << endl;
  }
}
コンパイル
Main.cpp:12:12: warning: exception of type 'int' will be caught by earlier handler [-Wexceptions]
 } catch (int i) {

このように、同じ型へのcatch文が複数存在するとコンパイル時にエラーとなります。

Javaのfinally文に相当する処理[編集]

shared_ptrや一時的なオブジェクトのデストラクタを使ってJavaのfinally相当の機能を実現することができるので、構文を追加するまでもない。

shared_ptrとラムダ式を使った例コード例
#include <memory>
#include <iostream>
#include <functional>

using namespace std;
using defer = shared_ptr<void>;    

int main() {
    defer finally(nullptr, bind([]{ cout << "Done!" << endl;  }));
    cout << "doSomething" << endl;
}
実行結果
doSamthing
Done!
一時的なオブジェクトのデストラクタを使った例
#include <iostream>
#include <stdexcept>
using namespace std;

int divide(int n, int d) {
  struct Defer {
    Defer() {}
    ~Defer() { std::cout << "fainally! "; }
  };
  try {
    Defer defer;
    if (n != 0 && d == 0)
      throw std::invalid_argument("Divide by zero exception");
    if (n == 0 && d == 0)
      throw std::invalid_argument("Zero divide by zero exception");

    return n / d;
  } catch (const std::invalid_argument &e) {
    std::cout << e.what() << " ";
    return 0;
  }
}

int main(void) {
  cout << "divide(1, 2) = " << divide(1, 2) << endl;
  cout << "divide(42, 3) = " << divide(42, 3) << endl;
  cout << "divide(1, 0) = " << divide(1, 0) << endl;
  cout << "divide(0, 0) = " << divide(0, 0) << endl;
}
実行結果
divide(1, 2) = fainally! 0
divide(42, 3) = fainally! 14
divide(1, 0) = fainally! Divide by zero exception 0
divide(0, 0) = fainally! Zero divide by zero exception 0

脚註[編集]

  1. ^ C++17 - cppreference.com” (2021年10月4日).テンプレート:Cite web/error
  2. ^ Dynamic exception specification (until C++17) - cppreference.com” (2021年12月16日).テンプレート:Cite web/error

関連項目[編集]