カスタム例外階層
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 82/104。
標準ライブラリには std::runtime_error のような便利な例外クラスが用意されていますが、実際のアプリケーションでは、カスタム例外階層を作成すると役立つことがよくあります。標準例外を継承する独自の例外クラスを定義することで、追加のコンテキストを持ち、より正確なエラー処理を可能にするドメイン固有のエラー型を作成できます。
カスタム例外クラスは通常、std::exceptionまたはその派生クラスのいずれかを継承し、what()メソッドをオーバーライドします。
#include <exception>
#include <string>
class DatabaseException : public std::exception {
std::string message;
public:
DatabaseException(const std::string& msg) : message(msg) {}
const char* what() const noexcept override { return message.c_str(); }
};
class ConnectionException : public DatabaseException {
public:
ConnectionException(const std::string& host)
: DatabaseException("Failed to connect to: " + host) {}
};
class QueryException : public DatabaseException {
public:
QueryException(const std::string& query)
: DatabaseException("Invalid query: " + query) {}
};この階層により、catch ブロックは例外をさまざまな具体性のレベルで処理できます。
try {
// データベース操作...
throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cout << "General error: " << e.what() << "\n";
}noexcept 指定子を what() に付けることは重要です。これは、そのメソッドが例外を送出しないことを保証するものであり、base クラスのインターフェースによって要求されています。C++ は最初に一致する handler を使用するため、catch ブロックは最も具体的なものから最も一般的なものの順に並べてください。
チャレンジ
簡単異なる種類のファイル関連エラーを処理する、カスタム例外階層を備えたファイル処理システムを構築しましょう。base 例外クラスを継承するドメイン固有の例外を作成し、さまざまな詳細度のレベルで正確なエラー処理を行えるようにします。
コードを3つのファイルに分けて整理します。
FileExceptions.h:ファイル操作用のカスタム例外階層を定義します。std::exceptionを継承するbaseFileExceptionクラスを作成します。エラーメッセージを保持し、noexcept指定子を付けてwhat()メソッドをオーバーライドします。次に、2つの派生例外クラスを作成します。
FileNotFoundException:filenameを受け取り、メッセージFile not found: [filename]を作成します。PermissionDeniedException:filenameを受け取り、メッセージPermission denied: [filename]を作成します。
各派生クラスは、適切なメッセージを指定して親のconstructorを呼び出します。
FileProcessor.h:ファイル操作をシミュレートし、カスタム例外をスローするFileProcessorクラスを定義します。FileProcessorには、ファイルを開く処理をシミュレートするメソッドopenFile(const std::string& filename)を実装します。- filenameに
missingが含まれている場合、FileNotFoundExceptionをスローします。 - filenameに
lockedが含まれている場合、PermissionDeniedExceptionをスローします。 - それ以外の場合は、文字列
Successfully opened: [filename]を返します。
- filenameに
main.cpp:入力から1つのfilenameを読み取ります。FileProcessorを作成し、ファイルを開こうとします。最も具体的なものから最も一般的なものの順にhandlersを並べたtry-catchブロックを使用します。FileNotFoundExceptionをCatchし、File Error: [message]を出力します。PermissionDeniedExceptionをCatchし、Access Error: [message]を出力します。FileExceptionをCatchし、General File Error: [message]を出力します。
例外がスローされなかった場合は、
openFile()が返した成功メッセージを出力します。
たとえば、入力がreport.txtの場合:
Successfully opened: report.txt入力がmissing_data.csvの場合:
File Error: File not found: missing_data.csv入力がlocked_config.iniの場合:
Access Error: Permission denied: locked_config.iniこのchallengeでは、カスタム例外階層によって適切な詳細度のレベルでエラーを処理できることを示します。正確な処理が必要な場合は特定の例外をCatchし、すべてのファイルエラーを一律に処理したい場合はbase例外をCatchできます。
自分で試してみよう
#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"
using namespace std;
int main() {
string filename;
cin >> filename;
FileProcessor processor;
// TODO: 例外を処理するために try-catch ブロックを使用する
// ハンドラを最も具体的なものから最も一般的なものへ順序付けする:
// 1. FileNotFoundException をキャッチする - 出力: "File Error: [message]"
// 2. PermissionDeniedException をキャッチする - 出力: "Access Error: [message]"
// 3. FileException をキャッチする - 出力: "General File Error: [message]"
// 例外がない場合、openFile() からの成功メッセージを出力する
try {
// TODO: processor.openFile() を呼び出し、結果を処理する
}
// TODO: 最も具体的なものから最も一般的なものの順に catch ブロックを追加する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ