例外を捕まえ損ねて terminate する原因と直し方
throwで投げた例外をどこのtry-catchでも捕まえられず、プログラム全体が異常終了してしまうバグです。C++のランタイムはstd::terminateを呼び出し、プロセスを強制終了させます。
エラーメッセージの読み方
terminate called after throwing an instance of 'std::runtime_error'
terminate called after throwing an instance of 'std::runtime_error'- ランタイム(または例外機構、assert)が不整合を検知して出したメッセージ
Aborted- この後プロセスは強制終了します(Aborted)
このエラーが出る典型パターン
パターン1
1 #include <iostream> 2 #include <stdexcept> 3 void withdraw(int balance, int amount) { 4 if (amount > balance) { 5 throw std::runtime_error("insufficient funds"); 6 } 7 std::cout << "ok" << std::endl; 8 } 9 int main() { 10 withdraw(100 , 500); ^ 11 return 0; 12 }
throwで投げた例外をどのtry-catchでも捕まえないと、C++のランタイムはstd::terminateを呼び出しプロセスを強制終了させます。ここでは残高を十分な値にして例外自体が発生しないようにするのが直し方です。
直し方: 100 を 1000 にします。
パターン2
1 #include <iostream> 2 #include <stdexcept> 3 void setAge(int age) { 4 if (age < 0) { 5 throw std::invalid_argument("age must not be negative"); 6 } 7 std::cout << "age set" << std::endl; 8 } 9 int main() { 10 setAge(-5); ^ 11 return 0; 12 }
「例外はthrowしておけば自動的にどこかで処理される」というのは誤解です。呼び出し元をさかのぼって対応するcatchが見つからなければ、mainまで届いてterminateされます。
直し方: -5 を 5 にします。
パターン3
1 #include <iostream> 2 #include <stdexcept> 3 void checkStock(int stock, int order) { 4 if (order > stock) { 5 throw std::runtime_error("out of stock"); 6 } 7 std::cout << "shipped" << std::endl; 8 } 9 int main() { 10 checkStock(3, 10); ^ 11 return 0; 12 }
例外の中身(what()で得られるメッセージ)はプログラマが自由に決められますが、捕まえられなければ結局terminateで強制終了する点は変わりません。
直し方: 10 を 2 にします。
パターン4
1 #include <iostream> 2 #include <stdexcept> 3 void deposit(int amount) { 4 if (amount < 0) { 5 throw std::invalid_argument("amount must not be negative"); 6 } 7 std::cout << "deposited" << std::endl; 8 } 9 int main() { 10 deposit(-10); ^ 11 return 0; 12 }
投げた例外を捕まえるcatchが無いと、mainまで届いてterminateされます。
直し方: -10 を 10 にします。
パターン5
1 #include <iostream> 2 #include <stdexcept> 3 void divide(int a, int b) { 4 if (b == 0) { 5 throw std::runtime_error("division by zero"); 6 } 7 std::cout << (a / b) << std::endl; 8 } 9 int main() { 10 divide(10, 0 ); ^ 11 return 0; 12 }
0除算を検知して例外を投げる設計でも、捕まえなければプロセスは強制終了します。
直し方: 0 を 2 にします。
よくある誤解
「例外はthrowしておけば自動的にどこかで処理される」というのは誤解です。C++の例外は、呼び出し元をさかのぼって対応するcatchが見つかるまで探されますが、mainまで誰も捕まえなければプログラムはterminateによって強制終了します。
実務での勘所
C++はJavaと違い、例外として投げられる値の型に制限がありません。throw 42;のように、std::exceptionを継承していない、ただの整数さえも例外として投げられます。catch (const std::exception&)のような特定の型を対象にしたcatchでは、こうした無関係な型の例外を拾えません。どんな型の例外が飛んできても最後の砦として受け止めたい場合は、catch (...)という、型を問わずすべてを捕まえる特別な構文を使います。ライブラリの境界など、何が飛んでくるか完全には把握できない場面での安全網として使われます。