C++例外処理をマスター:try、catch、throwの実践例
C++における例外処理とは
例外処理により、C++プログラムは予期せぬ実行時エラーから優雅に回復できます。エラーが発生すると、制御は特別なハンドラにジャンプし、どう対応するかを決定できます。
潜在的に問題のあるコードをtryブロックで囲み、それに1つ以上のcatch節を続けることで、プログラムに例外的状況を管理する明確なパスを与えます。
例外が投げられない場合、tryブロックは正常に実行され、ハンドラはバイパスされます。
このチュートリアルで学ぶ内容:
- 例外処理とは何か、なぜ重要か
- 核心キーワード:
throw、catch、try - 構文と一般的なパターン
- C++標準ライブラリが定義する標準例外
- ユーザー定義例外の作成と使用方法
なぜ例外処理が必要か
例外処理にはいくつかの具体的なメリットがあります:
- エラー処理ロジックを通常のコードから分離し、可読性と保守性を向上
- 関数が気にする例外のみを公開し、呼び出し側が残りを処理可能
例外処理のキーワード
C++例外処理の骨格をなす3つのキーワード:
- throw – 例外オブジェクトを作成してエラー状態を通知
- catch – 例外を捕捉し、それに対応するコードを提供
- try – 例外を投げる可能性のあるコード領域をマーク
tryブロック内のコードは保護コードと呼ばれ、これが投げると対応するcatchブロックが実行されます。
構文
try {
// 保護コード
} catch (Exception_Name exception1) {
// exception1を処理
} catch (Exception_Name exception2) {
// exception2を処理
}
// ... さらにcatchブロック
- 1つの
tryに複数のcatch節を続けられる - 各
catch節の型が捕捉する例外を指定 catch内部では、選択した識別子で捕捉した例外を参照可能
例1:vectorの範囲外アクセス
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> vec;
vec.push_back(0);
vec.push_back(1);
try {
vec.at(2); // 範囲外
} catch (exception& ex) {
cout << "Exception occurred!" << endl;
}
return 0;
}
出力:
コードのスクリーンショット:
コード分解:
- I/Oとコンテナサポートのため
iostreamとvectorをインクルード - 完全修飾名を避けるため
std名前空間を使用 vector<int>をインスタンス化し要素を追加tryブロック内でベクターの境界を超えた要素にアクセスを試行- 標準
std::exceptionを捕捉しメッセージ出力 mainから返却
例2:ゼロ除算の処理
#include <iostream>
using namespace std;
double zeroDivision(int x, int y) {
if (y == 0) {
throw "Division by Zero!";
}
return static_cast<double>(x) / y;
}
int main() {
int a = 11;
int b = 0;
double c = 0;
try {
c = zeroDivision(a, b);
cout << c << endl;
} catch (const char* message) {
cerr << message << endl;
}
return 0;
}
出力:
コードのスクリーンショット:
コード分解:
- ゼロ除算時にC文字列を投げる
zeroDivisionを定義 - 関数を
tryブロック内で呼び出し結果を出力 - 文字列例外を捕捉しエラーメッセージを出力
C++標準例外
標準ライブラリは<exception>で豊富な例外階層を宣言しています。よく使われるものを以下に示します:
| 例外 | 説明 |
|---|---|
| std::exception | すべての標準例外の基底クラス |
| std::bad_alloc | newの割り当て失敗時に投げられる |
| std::bad_cast | dynamic_castの失敗時に投げられる |
| std::bad_exception | 予期せぬ例外を通知するために使用 |
| std::bad_typeid | ヌルな多態的型のglvalueにtypeid適用時に投げられる |
| std::logic_error | プログラマが検出可能なエラー |
| std::domain_error | 数学関数の無効な定義域 |
| std::invalid_argument | 関数が無効な引数を受け取った |
| std::length_error | 過大な文字列作成の試み |
| std::out_of_range | vector::atでインデックスが範囲外の時に投げられる |
| std::runtime_error | 他のカテゴリで捕捉されない実行時エラー |
| std::overflow_error | 数学的オーバーフロー発生 |
| std::range_error | 値が演算の範囲外 |
| std::underflow_error | 数学的アンダーフロー発生 |
ユーザー定義例外
std::exception基底クラスを拡張してドメイン固有のエラー型を作成できます。what()をオーバーライドすると説明メッセージを提供できます。
例:カスタム例外
#include <iostream>
#include <exception>
using namespace std;
class NewException : public exception {
public:
const char* what() const noexcept override {
return "newException occurred";
}
} newex;
int main() {
try {
throw newex;
} catch (exception& ex) {
cout << ex.what() << '\n';
}
return 0;
}
出力:
コードのスクリーンショット:
コード分解:
NewExceptionをstd::exceptionから派生させwhat()をオーバーライド- 投げる用のオブジェクト
newexをインスタンス化 tryブロック内でnewexを投げるstd::exceptionへの参照で捕捉しwhat()でメッセージ出力
ポイント
- 例外処理は、実行時エラーから回復しなければならない堅牢なC++アプリケーションに不可欠
tryでコードを保護、catchで対応、throwで問題を通知- 豊富な標準例外を活用し、カスタムエラー型が必要なら
std::exceptionを拡張 - エラー処理ロジックをビジネスロジックから分離し、クリーンで保守しやすいコードに
C言語
- C++コメント完全ガイド:読みやすく保守しやすいコードのためのベストプラクティス
- C++演算子を徹底解説:種類、例、サンプルプログラム付き
- C++ switch文をマスター:構文、使い方、実践例
- C++動的配列の確保をマスター:コード例付き実践ガイド
- C++構造体を実例で徹底解説
- C++のstd::listをマスターする:構文、関数、実践例
- C++ structとclassの違い:構文、特徴、使い分けのベストプラクティス
- 2021年版:Windows/macOS/Linux対応C++ IDEおすすめ15選(無料・有料)
- Python例外処理マスター:try、except、else、finallyを徹底解説
- Java例外ハンドリング完全攻略:try-catch、finally、throw、throws
- Java例外ハンドリング実践:try-catch-finallyとリソース管理