C++/例外処理
例外処理 編集
例外処理(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
脚註 編集
- ^ “C++17 - cppreference.com” (2021年10月4日).テンプレート:Cite web/error
- ^ “Dynamic exception specification (until C++17) - cppreference.com” (2021年12月16日).テンプレート:Cite web/error