今回は、C++でよく出てくる「例外処理(try / catch)とは何か」について解説していきます。
「例外処理って何?」
「try / catch / throw の意味がわからない…」
「エラーが起きたとき、どう対処すればいいの?」
こんな疑問はありませんか?
プログラムは、ファイルが見つからない・配列の範囲外にアクセスした、など実行中に想定外のことが起きることがあります。例外処理を使うと、そんなときにいきなりクラッシュさせず、安全に対処できます。
この記事を読み終えると、あなたは例外処理の意味・try/catch/throwの使い方・実際の使いどころ・初心者がハマりやすいミスをしっかり理解できると思いますので、ぜひ最後まで読んでいただけると嬉しいです。
例外処理とは?
例外処理とは、エラー(例外)が起きたときに、プログラムを止めずに対処する仕組みです。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が安全
例外処理は、想定外のエラーからプログラムを守る安全装置です。まずはファイル読み込みや範囲外アクセスなど、「失敗しうる処理」から try / catch を使ってみてください。


