例外処理
プログラムの実行時、[必ず予期せぬ事態が発生する]:ファイル[が存在しない]、メモリ[不足]、[ネットワーク切断]……
[何もしなければ]、プログラム[はクラッシュする]。
例外処理[とは]、プログラムの[「エアバッグ」]のようなもの――[問題が発生した際]、[直接クラッシュするのではなく]、[優雅に対処する]ことである。
1. 例外処理[基礎]
(1) 1.1 例外とは何か?
例外[とは]プログラムの実行中に[発生する]エラー[または予期せぬ状況]のことです。
[よくある]例外:
- [ゼロ除算]
- [アクセス]ヌルポインタ
- ファイル[開くことに失敗しました]
- メモリ[割り当てに失敗しました]
(2) 1.2 例外処理[三要素]
C++の例外処理[3つのキーワードを使用]:
| [キーワード] | [機能] |
|---|---|
try |
[エラーが発生する可能性のある]コード |
catch |
[例外の捕捉と処理] |
throw |
例外をスローする |
(3) 1.3 [基本]例
例:[ゼロ除算]例外 (難易度 ⭐)
▶ サンプル 1: codeexample (難易度 ⭐)
#include iostream
int divide(int a, int b) {
if (b == 0) {
throw "0!"; // ...
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl; // ...
std::cout << divide(10, 0) << std::endl; // ...
}
catch (const char* msg) { // ...
std::cerr << ":" << msg << std::endl;
}
std::cout << "..." << std::endl;
return 0;
}
[実行結果]:
5:0!
💡 ヒント:
- 例外[が発生した後]、プログラム[はクラッシュしない]、[そのまま実行を続ける]
2. 例外タイプ
(1) 2.1 [スローされた]型
throw [投げられる][あらゆる]タイプ:
| タイプ | 例 |
|---|---|
| [基本]タイプ | throw 42; |
| 文字列 | throw "error"; |
| [標準]例外 | throw std::runtime_error("error"); |
| [カスタム]例外 | throw MyException(); |
(2) 2.2 [複数の]例外の捕捉
例:[異なる]type[の]例外を捕捉する (難易度 ⭐⭐)
#include iostream
### (3) ▶ 2:(⭐)
#include string
void process(int value) {
if (value == 0) {
throw 0; // int
}
if (value < 0) {
throw std::string("..."); // string
}
}
int main() {
try {
process(-5);
}
catch (int e) {
std::cerr << "int:" << e << std::endl;
}
catch (const std::string& e) {
std::cerr << "string:" << e << std::endl;
}
return 0;
}
3. [標準]例外
(1) 3.1 std::exception[階層]
C++の[標準ライブラリには一連の]例外クラスが定義されており、[それらはすべて] stdexcept [に含まれています]:
std::exception
├── std::logic_error
│ ├── std::invalid_argument
│ ├── std::out_of_range
│ └── std::length_error
└── std::runtime_error
├── std::overflow_error
└── std::underflow_error
(2) 3.2 [常用標準]の例外
| 例外クラス | [説明] |
|---|---|
std::invalid_argument |
[無効なパラメータ] |
std::out_of_range |
[越界] |
std::runtime_error |
実行時エラー |
std::bad_alloc |
メモリ[割り当て失敗](new[失敗]) |
例:[使用基準]例外 (難易度 ⭐⭐)
#include iostream
#include stdexcept
#include vector
int main() {
std::vectorint v = {1, 2, 3};
try {
v.at(10); // ...
}
catch (const std::out_of_range& e) {
std::cerr << ":" << e.what() << std::endl;
}
return 0;
}
4. [カスタム]例外
(1) 4.1 inheritancestd::exception
[ベストプラクティス]: [カスタム]例外クラスの継承 std::exception。
例:[カスタム]例外クラス (難易度 ⭐⭐⭐)
#include iostream
#include exception
#include string
// ...
class MyException : public std::exception {
private:
std::string msg;
public:
MyException(const std::string& msg) : msg(msg) {}
const char* what() const noexcept override {
return msg.c_str();
}
};
int main() {
try {
throw MyException("...");
}
catch (const MyException& e) {
std::cerr << ":" << e.what() << std::endl;
}
return 0;
}
5. 例外[安全]
(1) 5.1 例外[安全]とは何か?
例外[安全]とは、[たとえ]例外がスローされたとしても、プログラムが[一貫性のある状態]([リソースのリークがない]、[データが破損しない])を[維持できる]ことを指します。
[3つの保証レベル]:
- [基本保証]:プログラム[状態は一致している]、[ただし変化する可能性がある]
- [強保証]:[操作は成功するか]、[あるいは完全にロールバックされる]([何も起こらなかったかのように])
- [例外を投げない][保証]:この関数は[決して]例外を投げません
(2) 5.2 RAII――[リソース管理の強力なツール]
RAII(Resource Acquisition Is Initialization):[オブジェクト]がリソースを管理し、デストラクタが自動的にリソースを解放する。
例:[スマートポインタ]を使って[例外処理を][安全に][保証する] (難易度 ⭐⭐⭐)
#include iostream
#include memory
void process() {
// unique_ptr,
std::unique_ptrint p(new int(42));
// ... exception's ...
std::cout << *p << std::endl;
} // p
int main() {
try {
process();
}
catch (...) {
std::cerr << "..." << std::endl;
}
return 0;
}
6. noexcept[キーワード]
基本的な使い方
C++11[導入] noexcept、[宣言]function[は]例外をスローしない。
例:[声明不抛]例外 (難易度 ⭐)
void safeFunction() noexcept {
// ...
}
void unsafeFunction() {
// ...
}
💡 ヒント:
- デストラクタ[デフォルト]
noexcept noexcept[コンパイラの最適化に役立つ]
❓ よくある質問
Q:[いつ]exceptionを使うのですか? A:[真に予期せぬ][状況]に[使用し]、[通常の制御]には[使用しません]。
Q:catch(...)とは何ですか? A:[すべての]例外を[捕捉]し、[通常はリソースを解放した後、再び例外をスローするために使用されます]。
try {
// ...
}
catch (...) {
// ...
throw; // ...
}
Q:コンストラクタで[例外をスロー][できますか]? A:[できます]が、[メモリリークに注意]してください。[スマートポインタ][または]RAII[を使ってリソースを管理]してください。
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| try-catch-throw | 例外処理[三要素] |
| [標準]例外 | std::runtime_error[など] |
| [カスタム]例外 | 継承 std::exception |
| 例外[安全] | RAII[リソースのリークを防止] |
| noexcept | [宣言]関数[例外を投げない] |
📝 練習問題
-
**初心者向け(難易度 ⭐):[除算を行う]関数を書き、[除数が] 0 [の場合に] std::runtime_error をスローし、[main 関数内で] try/catch を使って [エラーを捕捉し]、[エラーメッセージを] 出力する。
-
**中級(難易度 ⭐⭐):[独自の]例外クラス(std::exceptionを継承)を定義し、[エラー]コードと[エラーの説明]という2つのフィールドを含め、throw文で投げ、catchブロックで捕捉する。
-
**上級(難易度 ⭐⭐⭐):[「リソースガード」]クラスを実装し、コンストラクタで[リソースを取得]し、デストラクタで[リソースを解放]するようにする。[途中で]例外がスローされた場合でも、[リソースは正しく解放される]ようにする(RAIIの[原理])。
- 例外処理[3つの要素]:try [監視]、throw [スロー]、catch [捕捉]
- [標準]例外クラスの継承[自] std::exception
- catch [按]type[一致],[複数] catch [上から順に]
- stack[展開]:exception[スローされた後、階層ごとに検索] catch
- noexcept [宣言]関数[例外を投げない]
[次のレッスン]:ファイル[応用操作](#41)



