C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で例外処理を実装するプログラムの書き方

複雑な数学的演算を行う関数があるとしましょう。その演算の過程では、さまざまな例外が発生する可能性があります。発生しうる例外の種類ごとに適切に処理し、次のように対応する必要があります。

  • コンピュータが計算用のメモリを確保できない場合は、「Memory Low!」と出力する
  • その他のC++関連の標準例外が発生した場合は、「Exception:」に続けて例外の内容を出力する
  • 上記以外の例外が発生した場合は、「Unhandled Exception」と出力する

ここでは、値のペアを格納した配列が与えられ、それを関数に渡す状況を考えます。例外が発生した場合はそれを捕捉して処理し、問題がなければ計算結果を出力します。

ポイントは、例外を投げる側の関数の内部機構には立ち入らず、例外を受け取って処理する側だけに注目することです。

たとえば、入力が arr = {{361643035132, 2297873642249}, {-17, 15}}; の場合、出力は次のようになります。

Memory Low!
Exception: val1 is negative

最初のペアの値は大きすぎて処理できないため、「Memory Low!」が出力されます。2番目のペアの最初の値は負の数であり、この関数は負の値を扱えないため、例外「val1 is negative」をスローします。

解決のアプローチ

この問題を解くには、以下の手順に従います。

  • i := 0 として初期化し、i < t の間、i を1ずつ増やしながらループ処理を行う
    • val1 := values[i][0] を代入する
    • val2 := values[i][1] を代入する
    • tryブロック内で foo(val1, val2) の結果を出力する
  • メモリの確保に失敗した場合(std::bad_alloc 例外を捕捉)
    • 「Memory Low!」を出力する
  • その他の標準C++例外 e を捕捉した場合
    • 「Exception: 」を出力し、続けて例外の詳細情報を出力する
  • それ以外の未知の例外を捕捉した場合
    • 「Unhandled Exception」を出力する

実装例

理解を深めるために、以下の実装例を見てみましょう。

#include <iostream>
#include <exception>
#include <string>
#include <stdexcept>
#include <vector>
#include <cmath>
using namespace std;

class Solution {
private:
    static int value;
public:
    static int foo(long long val1, long long val2) {
        value += 1;
        if(val1 < 0) {
            throw std::invalid_argument("val1 is negative");
        }
        vector<int> vectorList(val1, 0);
        int r = -1, c = sqrt(-1);
        if(val2 == 0) throw 0;
        r = (val1 / val2) * r;
        int ans = vectorList.at(val2);
        return r + val1 - val2 * ans;
    }
    static int getVal() {
        return value;
    }
};
int Solution::value = 0;

void solve(int t, long int values[][2]) {
    for (int i = 0; i < t; i++) {
        long long val1, val2;
        val1 = values[i][0];
        val2 = values[i][1];
        try {
            cout << Solution::foo(val1, val2) << '\n';
        }
        catch (const std::bad_alloc&) {
            cout << "Memory Low!\n";
        }
        catch (const std::exception& e) {
            cout << "Exception: " << e.what() << '\n';
        }
        catch (...) {
            cout << "Unhandled Exception\n";
        }
    }
}
int main() {
    long int arr[][2] = {{361643035132, 2297873642249}, {-17, 15}};
    solve(2, arr);
}

入力

long int arr[][2] = {{361643035132, 2297873642249}, {-17, 15}};
solve(2, arr);

出力

Memory Low!
Exception: val1 is negative

このように、複数のcatch句を組み合わせることで、メモリ不足・標準例外・未知の例外という3つのケースを段階的に振り分けて処理できます。catch句は記述した順序に評価されるため、より具体的な例外型(ここでは std::bad_alloc)を先に、汎用的な std::exception を後に、最後に catch(...) を配置するのがC++の例外処理における定石です。

  1. C++プログラムにおける二分探索(バイナリサーチ)の基本と実装

    二分探索(バイナリサーチ)とは二分探索は「半区間探索」「対数探索」「バイナリチョップ」とも呼ばれる検索アルゴリズムで、ソート済みの配列の中から目的の値が存在する位置を効率的に見つけ出します。基本的な仕組みは非常にシンプルです。まず、探したい値(ターゲット値)を配列の中央の要素と比較します。一致しなかった場合は、ターゲット値が存在し得ない半分を丸ごと排除し、残りの半分に対して同様の比較を繰り返します。この「中央との比較」と「範囲の絞り込み」を続け、ターゲット値が見つかるか、検索範囲が空になる(=配列にその値が存在しない)かのどちらかで処理が終了します。アイデア自体は簡単ですが、正しく実装するには

  2. C++の例外処理入門!try・catch・throwの基本と標準例外クラス一覧

    C++における例外処理(Exception Handling)とは、プログラムの実行時に発生するエラー(ランタイムエラー)を適切に扱うための仕組みです。例外とは、実行時に送出(スロー)されるイベントのことで、C++ではすべての標準例外が std::exception クラスから派生しています。もし例外を捕捉しなかった場合、例外メッセージが出力され、プログラムは強制終了してしまいます。 C++標準では、例外関連のクラスが <exception> ヘッダに定義されており、プログラムの中で自由に利用できます。親子関係によるクラス階層は次のようになっています。 C++の主な標準例外クラス