C++: 例外処理

最終更新:2026-08-31

プログラム実行時、エラーは避けられません:ファイルが見つからない、メモリ不足、ネットワーク障害……

しかし、プログラムはクラッシュしてはいけません。

例外処理はプログラムの「安全ネット」——問題が起きても、適切に処理し、ユーザーに伝え、ログを残せます。


1. 例外処理の基本

(1) 1.1 例外とは?

例外はプログラム実行時のエラー状態を表します。

主な例外の種類:


(2) 1.2 例外処理の仕組み

C++の例外処理は3つのキーワードを使用します:

キーワード 機能
try 例外を監視するコード
catch 例外を捕捉して処理
throw 例外を投げる

(3) 1.3 基本的な例

▶ サンプル 1:コード例(難易度 ⭐)

CPP
#include <iostream>

int divide(int a, int b) {
    if (b == 0) {
        throw "ゼロ除算エラー!"; // 例外を投げる
    }
    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;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
5
エラー:ゼロ除算エラー!
プログラム終了
💡 ヒント:

  • 例外が発生しても、プログラムはクラッシュせず、catchブロックにジャンプして処理を継続します



2. 例外の型

(1) 2.1 投げられる型

throw任意の型を投げられます:

基本型 throw 42;
文字列 throw "エラー";
標準例外 throw std::runtime_error("エラー");
カスタム例外 throw MyException();

(2) 2.2 複数の例外

▶ サンプル:複数型の例外(難易度 ⭐⭐)

CPP
#include <iostream>
### ▶ サンプル 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> ヘッダ:

TEXT 📖 参照専用
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

▶ サンプル:標準例外の使用(難易度 ⭐⭐)

CPP
#include <iostream>
#include <stdexcept>
#include <vector>

int main() {
    std::vector<int> 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 std::exceptionを継承

推奨: カスタム例外クラスは std::exception を継承します。

▶ サンプル:カスタム例外クラス(難易度 ⭐⭐⭐)

CPP
#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 スタックアンワインディングとは?

スタックアンワインディング:例外が投げられた時、プログラムは関数呼び出しスタックを遡り、適切なcatchブロックを見つけるまで、各スコープのローカルオブジェクトのデストラクタを実行します。

重要なポイント:

  1. 基本原則: プログラムはスタックを遡り、各関数のローカルオブジェクトのデストラクタを呼ぶ
  2. リソース管理: メモリ、ファイルハンドラなど(デストラクタで解放)
  3. 例外安全: 関数が例外を投げてもリソースがリークしないこと

(2) 5.2 RAII——リソース管理のベストプラクティス

RAII(Resource Acquisition Is Initialization):オブジェクトの生命周期でリソースを管理し、デストラクタで解放します。

▶ サンプル:スマートポインタで例外時の解放(難易度 ⭐⭐⭐)

CPP
#include <iostream>
#include <memory>

void process() {
    // unique_ptrで確保、例外が起きても自動解放
    std::unique_ptr<int> p(new int(42));
    
    // ... 例外が発生する可能性のある処理 ...
    
    std::cout << *p << std::endl;
} // pのデストラクタが呼ばれ、メモリ解放

int main() {
    try {
        process();
    }
    catch (...) {
        std::cerr << "例外が発生しました" << std::endl;
    }
    return 0;
}
▶ 試してみよう


6. noexcept指定子

(1) 6.1 基本的な使い方

C++11の noexcept は、関数が例外を投げないことを宣言します。

▶ サンプル:例外を投げない関数(難易度 ⭐)

TEXT 📖 参照専用
void safeFunction() noexcept {
    // 例外を投げない安全な関数
}

void unsafeFunction() {
    // 例外を投げる可能性がある
}
💡 ヒント:

  • デストラクタは暗黙的に noexcept です
  • noexcept はコンパイラの最適化に役立ちます

▶ サンプル 2:複数のcatchブロック(難易度 ⭐⭐)

CPP
#include <iostream>
#include <stdexcept>

void processData(int value) {
    if (value < 0) {
        throw std::invalid_argument("負の値は許可されていません");
    }
    if (value > 100) {
        throw std::out_of_range("値が上限を超えています");
    }
    std::cout << "処理中: " << value << std::endl;
}

int main() {
    int testValues[] = {50, -5, 150};

    for (int val : testValues) {
        try {
            processData(val);
        } catch (const std::invalid_argument& e) {
            std::cout << "無効な引数: " << e.what() << std::endl;
        } catch (const std::out_of_range& e) {
            std::cout << "範囲外: " << e.what() << std::endl;
        }
    }

    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
処理中: 50
無効な引数: 負の値は許可されていません
範囲外: 値が上限を超えています

▶ サンプル 3:ネストした例外処理(難易度 ⭐⭐)

CPP
#include <iostream>
#include <stdexcept>

void innerFunction(int value) {
    if (value < 0) {
        throw std::invalid_argument("負の値は許可されていません");
    }
    std::cout << "内部関数が処理しました: " << value << std::endl;
}

void outerFunction(int value) {
    try {
        innerFunction(value);
    } catch (const std::invalid_argument& e) {
        std::cout << "外部で捕捉: " << e.what() << std::endl;
        throw;  // mainに再スロー
    }
}

int main() {
    try {
        outerFunction(10);
        outerFunction(-5);
    } catch (const std::exception& e) {
        std::cout << "mainで捕捉: " << e.what() << std::endl;
    }

    std::cout << "プログラム終了" << std::endl;
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
内部関数が処理しました: 10
外部で捕捉: 負の値は許可されていません
mainで捕捉: 負の値は許可されていません
プログラム終了

❓ よくある質問

Q:いつ例外を使う? A:回復可能なエラー状況で、エラー処理コードを通常のフローから分離したい場合。ストリームなどで使用。


Q:catch(...) とは? A:すべての型の例外を捕捉します。ログを残して再スローする場合に使用。

CPP
try {
    // ...
}
catch (...) {
    // ログを記録
    throw; // 再スロー
}

Q:コンストラクタで例外を投げると? A:オブジェクトは生成されませんが、メモリリークの可能性があります。スマートポインタやRAIIでリソース管理を。


📖 まとめ

トピック 要点
try-catch-throw 例外処理の基本構文
標準例外 std::runtime_error など
カスタム例外 std::exception を継承
スタックアンワインディング RAIIでリソース管理
noexcept 関数が例外を投げないことを宣言

📝 練習問題

  1. 初級(難易度 ⭐): 除算関数を書き、0で除算時に std::runtime_error を投げ、main関数で try/catch してエラーを出力してください。

  2. 中級(難易度 ⭐⭐): カスタム例外クラス(std::exception を継承)を定義し、ファイルオープンエラーとネットワークエラーを区別して、throwとcatchを試してください。

  3. 上級(難易度 ⭐⭐⭐): 「リソース管理」クラスを実装し、コンストラクタでリソースを確保し、デストラクタで解放。例外を投げてもリークしないことを確認(RAII)。



次のレッスン:ファイル操作応用(#41)

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%