OOPにおける例外処理
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 81/104。
C++における例外処理は、実行時エラーを検出し、それに対応するための構造化された手法を提供します。オブジェクト指向プログラミングでは、例外はクラス階層と自然に統合され、何が問題だったのかというコンテキストを持つ、意味のあるエラー型を作成することができます。
基本的なメカニズムは、try、throw、および catch という3つのキーワードを使用します。失敗する可能性のあるコードは try ブロック内に記述し、エラーは throw で通知され、catch ブロックが特定の例外タイプを処理します:
#include <iostream>
#include <stdexcept>
class BankAccount {
double balance;
public:
BankAccount(double b) : balance(b) {}
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
double getBalance() const { return balance; }
};
int main() {
BankAccount account(100.0);
try {
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}
std::cout << "Balance: " << account.getBalance() << "\n";
}C++標準ライブラリは、std::exceptionをルートとする例外クラスの階層を提供します。一般的な派生クラスには、実行時の問題のためのstd::runtime_errorや、プログラミング上のミスのためのstd::logic_errorが含まれます。すべての標準例外は、エラーメッセージを返すwhat()メソッドを提供します。
例外がスローされると、スタックの巻き戻しが発生します。プログラムが一致するcatchブロックを検索する際、すべてのローカルオブジェクトに対してデストラクタが呼び出されます。これにより、エラーが発生した場合でも適切なクリーンアップが保証されます。これが、RAIIと例外処理が非常にうまく連携する理由です。
チャレンジ
簡単オブジェクト指向の文脈における例外処理を実演する、安全な割り算計算機を作成しましょう。割り算を実行し、問題が発生したときに意味のある例外をスローする Calculator クラスを作成し、メインプログラムでそれらの例外を適切に処理します。
コードは以下の3つのファイルに分けて構成します。
Calculator.h: 適切なエラー処理を伴う数学的演算を実行するCalculatorクラスを定義します。Calculatorには、割り算の結果を返すdivide(double numerator, double denominator)メソッドが必要です。ただし、分母がゼロの場合は、メッセージ"Division by zero"を持つstd::runtime_errorをスローする必要があります。分子または分母のいずれかが負の場合は、メッセージ"Negative values not allowed"を持つstd::invalid_argumentをスローする必要があります。また、値の平方根を返す
safeSqrt(double value)メソッドも含めてください。値が負の場合は、メッセージ"Cannot compute square root of negative number"を持つstd::runtime_errorをスローします。sqrt関数のために<cmath>をインクルードする必要があります。Calculator.cpp: ヘッダーファイルで宣言されたメソッドを実装します。必要なヘッダー(<stdexcept>と<cmath>)をインクルードし、演算を実行する前に条件を確認してください。main.cpp: 3つの入力(それぞれ別の行)を読み取ります。- 操作タイプ:
"divide"または"sqrt" - 1番目の数値 (double)
- 2番目の数値 (double) — 割り算にのみ使用され、sqrt の場合は無視されます
Calculatorオブジェクトを作成し、try ブロック内で要求された操作を実行します。操作が成功した場合は、"Result: [value]"と出力します。例外がキャッチされた場合は、"Error: [message]"と出力します。ここで、メッセージは例外のwhat()メソッドから取得します。try-catch ブロックの後(例外が発生したかどうかに関わらず)、例外処理後もプログラムの実行が継続されることを示すために
"Calculation complete"と出力します。- 操作タイプ:
例えば、入力が "divide"、10、2 の場合:
Result: 5
Calculation complete入力が "divide"、10、0 の場合:
Error: Division by zero
Calculation complete入力が "divide"、-5、2 の場合:
Error: Negative values not allowed
Calculation complete入力が "sqrt"、16、0 の場合:
Result: 4
Calculation complete入力が "sqrt"、-9、0 の場合:
Error: Cannot compute square root of negative number
Calculation complete例外をキャッチして意味のあるメッセージを表示し、その後実行を継続することで、プログラムがいかに適切にエラーを処理しているかに注目してください。このパターンにより、エラー処理をクリーンに保ち、メインのロジックを正常なパスに集中させることができます。
チートシート
C++における例外処理では、try、throw、catchという3つのキーワードを使用します。失敗する可能性のあるコードはtryブロック内に記述し、エラーはthrowで通知され、catchブロックで特定の例外タイプを処理します。
try {
// Code that might throw an exception
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}クラスメソッドから例外をスローする場合:
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}C++標準ライブラリは、std::exceptionをルートとする例外クラスを提供しています。一般的な型には以下が含まれます:
std::runtime_error- 実行時の問題用std::logic_error- プログラミング上のミス用std::invalid_argument- 無効な引数用
すべての標準例外は、エラーメッセージを返すwhat()メソッドを提供しています。
例外がスローされると、スタックの巻き戻しが発生します。プログラムが一致するcatchブロックを検索する際、すべてのローカルオブジェクトに対してデストラクタが呼び出され、適切なクリーンアップが保証されます。
標準例外クラスを使用するには、<stdexcept>をインクルードしてください。
自分で試してみよう
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// 入力を読み込む
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Calculator オブジェクトを作成する
Calculator calc;
// TODO: try-catch ブロックを使用して演算を実行する
// - operation が "divide" の場合、calc.divide(num1, num2) を呼び出す
// - operation が "sqrt" の場合、calc.safeSqrt(num1) を呼び出す
// - 成功した場合、"Result: " に続けて結果を表示する
// - 例外をキャッチし、"Error: " に続けて例外メッセージを表示する (what() を使用)
// - try-catch の後、常に "Calculation complete" を表示する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス