注意
ここで説明されている例外処理の仕様は、C++11でnoexcept機能が搭載されたことにより非推奨となった動的例外仕様(dynamic exception specification)です。動的例外仕様はC++-17で廃止されました[1][2]

例外処理 編集

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

例外処理とは 編集

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

try {
  例外を捕捉する必要のある処理
}
catch( e){
   例外発生時の処理;
}
もし try に続くブロックで例外が発生した場合 catch で続くブロックの処理が実行されます。
try {
   throw 2;
}
catch(const int i){
   cout << "エラーが発生しました。値は" << i << "です。" << 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( 変数名){
     例外発生時の処理;
   }

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

関連項目 編集