ファイルが見つからない。配列の範囲外にアクセスした。プログラムは、実行中に、想定外のことが起きることがあります。そのたびに、いきなりクラッシュしていては困ります。
例外処理を使うと、こうした失敗を、安全に受け止められます。この記事では、throw・try・catchの意味と使い方、実際の使いどころ、初心者がハマりやすいミスを解説します。
例外処理とは?
例外処理とは、エラー(例外)が起きたときに、プログラムを止めずに対処する仕組みです。3つのキーワードで動きます。
throw… 例外を投げる(エラーを知らせる)try… 例外が起きるかもしれない処理を囲むcatch… 投げられた例外を受け取って対処する
#include <iostream>
int main() {
try {
int hp = -5;
if (hp < 0) {
throw "HPがマイナスです"; // 例外を投げる
}
std::cout << "HP: " << hp << "\n";
}
catch (const char* msg) {
std::cout << "エラー: " << msg << "\n"; // 受け取って対処
}
return 0;
}エラー: HPがマイナスです
throw した瞬間に処理が中断され、catch に飛んで対処が行われます。プログラムは落ちずに続けられます。
なぜ例外処理が必要?
理由は、エラーを無視すると、そのままクラッシュしたり誤動作したりするからです。戻り値でエラーをいちいちチェックする方法もありますが、例外を使うと正常な処理とエラー対処をきれいに分けて書けるので見通しが良くなります。
標準の例外クラスを使う
実際には、文字列ではなく標準の例外クラスを使うのが一般的です。std::runtime_error などがあり、what() でメッセージを取り出せます。
#include <iostream>
#include <stdexcept>
int main() {
try {
throw std::runtime_error("セーブデータの読み込みに失敗");
}
catch (const std::exception& e) {
std::cout << "例外: " << e.what() << "\n";
}
return 0;
}例外: セーブデータの読み込みに失敗
catch (const std::exception& e) と書いておくと、標準の例外をまとめて受け取れます。
実践例:範囲外アクセスを検知する
std::vector の at() は、範囲外にアクセスすると std::out_of_range を投げてくれます。[] と違って安全にエラーを検知できます(vectorの基本は配列とvectorの違いの記事へ)。
#include <iostream>
#include <vector>
#include <stdexcept>
int main() {
std::vector<int> enemies = {10, 20, 30};
try {
std::cout << enemies.at(5) << "\n"; // 範囲外!
}
catch (const std::out_of_range& e) {
std::cout << "範囲外アクセス: " << e.what() << "\n";
}
return 0;
}範囲外アクセス: invalid vector subscript
【重要】私が実際に例外処理で困った体験談
個人開発で、敵リストを vector の [] でアクセスしていたとき、範囲外なのにエラーも出ずに変な値を読んで、原因不明のクラッシュに数時間悩まされました。
試しに [] を at() に変えたところ、out_of_range の例外がはっきり出て、どこで範囲外を踏んでいるか一発で特定できました。デバッグ中は at() が心強い、と学んだ出来事でした。デバッグの基本はデバッグのやり方の記事もどうぞ。
例外処理使用時のよくある失敗例と対処法
①catchの型が合っていない
投げた型と catch の型が違うと受け取れません。標準例外は catch (const std::exception& e) で広く受けるのが基本です。
②例外を握りつぶす(空のcatch)
catch の中を空にすると、エラーが起きてもなかったことにされます。最低でもログを出すなど、握りつぶさないようにしましょう。
③例外でリソースが漏れる
途中で例外が飛ぶと delete が実行されず、メモリリークになることがあります。スマートポインタなどのRAIIで持てば、例外が飛んでも自動で後始末されて安全です。
注意点
- 標準例外は
const std::exception&で受け取る - 例外は握りつぶさない(せめてログを残す)
- 毎フレーム回るゲームループ内で多用しない(例外は重め)。想定内の分岐は
ifで
ここまでのおさらい
- 例外処理はエラー時にプログラムを止めずに対処する仕組み
throwで投げ、tryで囲み、catchで受け取る- 標準例外は
what()でメッセージを取れる - 範囲外検知には
at()、リソース管理にはRAIIが安全
throwで投げて、tryで囲み、catchで受け取ります。標準例外のメッセージは、what()で取れます。範囲外の検知にはat()、リソースの管理には、RAIIが安全です。


