【C++】標準例外クラスの使い方 | out_of_range 等のエラーを使い分ける

目次

はじめに

C++でエラーが発生した際、単に throw "エラーメッセージ"; のように文字列を投げるのではなく、標準ライブラリ <stdexcept> などで定義されている標準例外クラスを使うのが良い習慣です。

これらのクラスは std::exception を基底クラスとする階層構造を持っており、catch ブロックで特定の種類のエラーだけを捕捉したり、エラーの大まかな分類で捕捉したりと、柔軟なエラーハンドリングを可能にします。

この記事では、主要な標準例外クラスの紹介と、その中の一つ std::out_of_range を使った具体的な例を解説します。


主要な標準例外クラス

<stdexcept> で定義されている主な例外クラスは、大きく2つのグループに分けられます。

  • 論理エラー (logic_error): プログラムのロジックのバグが原因で発生する、実行前に発見できるはずのエラー。
  • 実行時エラー (runtime_error): プログラムの実行中に、外部要因などによって発生する、実行前に予測できないエラー。
基底クラス例外クラス説明
logic_errorinvalid_argument関数に渡された引数が不正
domain_error関数の定義域外の引数が渡された
length_errorオブジェクトが許容する最大長を超えた
out_of_range範囲外のインデックスでアクセスしようとした
runtime_errorrange_error内部計算の結果が有効範囲外になった
overflow_error算術オーバーフローが発生した
underflow_error算術アンダーフローが発生した

標準例外クラスを使ったサンプルコード

このコードは、配列のインデックスが有効範囲内にあるかをチェックし、範囲外であれば std::out_of_range 例外をスローします。main関数は、try-catch ブロックでその例外を捕捉します。

完成コード

#include <iostream>
#include <vector>
#include <stdexcept> // out_of_range, exception など

using namespace std;

// vectorの指定したインデックスの値を返す関数
int get_value_at(const vector<int>& data, int index) {
    // インデックスが有効範囲外なら、out_of_range例外をスロー
    if (index < 0 || index >= data.size()) {
        throw out_of_range("指定されたインデックスは範囲外です。");
    }
    
    return data[index];
}

int main() {
    vector<int> numbers = {10, 20, 30};

    try {
        // 1. 成功するケース
        cout << "インデックス1の値: " << get_value_at(numbers, 1) << endl;

        // 2. 失敗するケース (範囲外のインデックスを指定)
        cout << "インデックス5の値: " << get_value_at(numbers, 5) << endl;
    }
    // 3. out_of_range例外を捕捉する
    catch (const out_of_range& e) {
        cerr << "エラー検知: " << e.what() << endl;
        // 捕捉した例外を、さらに呼び出し元に投げることもできる
        // throw;
    }
    // 汎用的なexceptionで、他の種類の例外も捕捉できる
    catch (const exception& e) {
        cerr << "予期せぬエラー: " << e.what() << endl;
    }
    
    return 0;
}

コードの解説

throw out_of_range(...)

throw キーワードを使って、std::out_of_range クラスのオブジェクトを生成し、それを「例外」として送出(スロー)しています。コンストラクタの引数に、エラーの詳細を説明する文字列を渡すのが一般的です。

catch (const out_of_range& e)

tryブロック内でスローされた例外を、catchブロックで捕捉(キャッチ)します。

  • const out_of_range& e: out_of_range 型(またはその親クラスの型)の例外だけを、このブロックで捕捉することを意味します。
  • e.what(): 捕捉した例外オブジェクト e.what() メンバ関数を呼び出すと、throw 時にコンストラクタに渡したエラーメッセージ文字列を取得できます。

throw;

catchブロックの最後で throw; とだけ書くと、捕捉したのと同じ例外オブジェクトを、そのまま再度スローすることができます。これにより、現在の関数でログ記録などの部分的なエラー処理を行った後、最終的な処理をさらに呼び出し元の関数に委ねる、といった階層的なエラーハンドリングが可能になります。


まとめ

今回は、C++の標準例外クラスを使い分けて、エラーの種類を明確に表現する方法を解説しました。

  • エラーの種類に応じて、out_of_rangeinvalid_argument といった、意味的に最も近い例外クラスを選択して throw する。
  • catch ブロックを複数用意することで、例外の型ごとに異なるエラー処理を記述できる。

適切な例外クラスを使い分けることで、コードを読む人(将来の自分も含む)が、どのような種類のエラーが発生しうるのかを理解しやすくなり、プログラムの堅牢性が向上します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次